Files
trackgo-fe/apps/web/src/apps/main/modules/sales/shared/sales-document.mapper.ts
T
shancheas 4d9c83ee83 feat: implement sales management module with product and order handling
- Introduced a new Sales module, consolidating routes for managing sales requests and orders.
- Added a Products module for handling product details, including creation, editing, and viewing functionalities.
- Implemented UI components for product forms and detail views, with validation schemas for product data.
- Integrated language support for English and Indonesian in the new modules.
- Developed unit tests for sales and product remote data services and transformers to ensure functionality and reliability.

These changes enhance the application by providing a structured approach to sales management, improving user experience and data handling.
2026-08-26 15:29:46 +07:00

151 lines
5.0 KiB
TypeScript

import { formatDateValue, parseDateValue } from '@repo/ui/form';
import { emptyToNull, omitEmptyFields } from '../../../../../core/domain/configuration-field-validators';
import type { SalesDocumentDto, SalesDocumentEntity, SalesImageEntity, SalesLineEntity } from './sales-document.entity';
export function relationId(value: unknown): string | undefined {
if (value && typeof value === 'object' && 'id' in value) {
const id = (value as { id?: unknown }).id;
return id == null ? undefined : String(id);
}
return undefined;
}
export function toRelationStub(id?: string | null) {
return id ? { id } : null;
}
export function mapSalesLinesFromDto(dto: SalesDocumentDto): SalesLineEntity[] {
return (dto.products ?? []).map((line) => ({
id: line.id,
productId: line.productId,
product: toRelationStub(line.productId),
quantity: line.quantity,
price: line.price ?? null,
}));
}
export function mapSalesImagesFromDto(dto: SalesDocumentDto): SalesImageEntity[] {
return (dto.images ?? []).map((image) => ({
id: image.id,
url: image.url,
description: image.description ?? null,
}));
}
export function mapSalesDocumentFromDto(dto: SalesDocumentDto): SalesDocumentEntity {
return {
id: dto.id,
code: dto.code ?? null,
date: formatDateValue(parseDateValue(dto.date) ?? undefined),
salesPersonId: dto.salesPersonId,
salesPerson: toRelationStub(dto.salesPersonId),
branchId: dto.branchId,
branch: toRelationStub(dto.branchId),
divisionId: dto.divisionId,
division: toRelationStub(dto.divisionId),
customerId: dto.customerId,
customer: toRelationStub(dto.customerId),
address: dto.address,
latitude: dto.latitude ?? null,
longitude: dto.longitude ?? null,
notes: dto.notes ?? null,
products: mapSalesLinesFromDto(dto),
images: mapSalesImagesFromDto(dto),
status: dto.status,
createdAt: dto.createdAt,
updatedAt: dto.updatedAt,
createdBy: dto.createdBy,
updatedBy: dto.updatedBy,
};
}
export function toSalesWritePayload(
entity: Partial<SalesDocumentEntity>,
options?: { includeSalesRequestId?: boolean },
): Record<string, unknown> {
const products = (entity.products ?? [])
.map((line) => {
const productId = relationId(line.product) ?? line.productId;
if (!productId) return null;
return omitEmptyFields({
productId,
quantity: line.quantity,
price: line.price,
});
})
.filter(Boolean);
const images = (entity.images ?? [])
.filter((image) => Boolean(image?.url))
.map((image) => omitEmptyFields({ url: image.url, description: image.description }));
const payload: Record<string, unknown> = {
code: entity.code,
date: formatDateValue(entity.date),
salesPersonId: relationId(entity.salesPerson) ?? entity.salesPersonId,
branchId: relationId(entity.branch) ?? entity.branchId,
divisionId: relationId(entity.division) ?? entity.divisionId,
customerId: relationId(entity.customer) ?? entity.customerId,
address: entity.address,
latitude: emptyToNull(entity.latitude),
longitude: emptyToNull(entity.longitude),
notes: emptyToNull(entity.notes),
products,
images,
};
if (options?.includeSalesRequestId) {
payload.salesRequestId = relationId((entity as { salesRequest?: unknown }).salesRequest) ?? (entity as { salesRequestId?: string }).salesRequestId;
}
return payload;
}
export function toSalesFilterPayload(filter: Record<string, any>): Record<string, any> {
const next = { ...filter };
if (next.salesPerson && typeof next.salesPerson === 'object') {
next.salesPersonId = next.salesPerson.id;
delete next.salesPerson;
}
if (next.branch && typeof next.branch === 'object') {
next.branchId = next.branch.id;
delete next.branch;
}
if (next.division && typeof next.division === 'object') {
next.divisionId = next.division.id;
delete next.division;
}
if (next.customer && typeof next.customer === 'object') {
next.customerId = next.customer.id;
delete next.customer;
}
if (next.salesRequest && typeof next.salesRequest === 'object') {
next.salesRequestId = next.salesRequest.id;
delete next.salesRequest;
}
return omitEmptyFields(next);
}
export function salesRequestToFormValues(request: SalesDocumentEntity) {
return {
date: request.date,
salesPerson: request.salesPerson ?? toRelationStub(request.salesPersonId),
branch: request.branch ?? toRelationStub(request.branchId),
division: request.division ?? toRelationStub(request.divisionId),
customer: request.customer ?? toRelationStub(request.customerId),
address: request.address,
latitude: request.latitude ?? null,
longitude: request.longitude ?? null,
notes: request.notes ?? '',
products: (request.products ?? []).map((line) => ({
product: line.product ?? toRelationStub(line.productId),
quantity: line.quantity,
price: line.price ?? '',
})),
images: (request.images ?? []).map((image) => ({
url: image.url,
description: image.description ?? '',
})),
};
}