Files
trackgo-be/src/modules/field/shared/field-fields.ts
T
shancheas c9f9b31abf Add field management module with database schema and validation
- Introduced `FieldModule` to manage cycles and plans, including read and write controllers.
- Created database migrations for `company_settings`, `cycles`, `cycle_weekdays`, `cycle_destinations`, `plans`, `plan_destinations`, `plan_invoices`, and `plan_packing_slips` tables, including constraints and unique indexes.
- Developed service and repository layers for handling cycle and plan data operations.
- Added unit tests for the cycles and plans services, repositories, and controllers to ensure functionality and correctness.
- Updated application module to include the new `FieldModule` for better organization.
2026-08-25 18:20:19 +07:00

94 lines
2.3 KiB
TypeScript

import type { WeekdayName } from './field-purpose';
import { isWeekdayName } from './field-purpose';
export type WeekdayTemplateInput = {
readonly startBranchId: string;
readonly endBranchId: string;
readonly customerIds: readonly string[];
};
export type WeekdaysInput = Partial<Record<WeekdayName, WeekdayTemplateInput>>;
export function isValidCycleNumber(raw: number): boolean {
return Number.isInteger(raw) && raw >= 1;
}
export function assertCompleteWeekday(
raw: WeekdayTemplateInput | undefined,
): WeekdayTemplateInput | undefined {
if (raw === undefined) {
return undefined;
}
if (
!raw.startBranchId ||
!raw.endBranchId ||
!Array.isArray(raw.customerIds) ||
raw.customerIds.length === 0
) {
throw new Error('incomplete');
}
return raw;
}
export function parseWeekdaysInput(raw: unknown): WeekdaysInput {
if (raw === undefined || raw === null) {
return {};
}
if (typeof raw !== 'object' || Array.isArray(raw)) {
throw new Error('incomplete');
}
const record = raw as Record<string, WeekdayTemplateInput>;
const result: WeekdaysInput = {};
for (const [key, value] of Object.entries(record)) {
if (!isWeekdayName(key)) {
throw new Error('incomplete');
}
const complete = assertCompleteWeekday(value);
if (complete) {
result[key] = complete;
}
}
return result;
}
/** RFC 4180-style record split that preserves commas inside quotes. */
export function parseCsvRecord(line: string): string[] {
const cells: string[] = [];
let current = '';
let inQuotes = false;
for (let i = 0; i < line.length; i++) {
const ch = line[i];
if (inQuotes) {
if (ch === '"') {
if (line[i + 1] === '"') {
current += '"';
i += 1;
} else {
inQuotes = false;
}
} else {
current += ch;
}
} else if (ch === '"') {
inQuotes = true;
} else if (ch === ',') {
cells.push(current.trim());
current = '';
} else {
current += ch;
}
}
cells.push(current.trim());
return cells;
}
export function isAllowedCsvUpload(file: {
mimetype: string;
originalname: string;
}): boolean {
return (
file.mimetype.includes('csv') ||
file.originalname.toLowerCase().endsWith('.csv')
);
}