diff --git a/runtime/server.cjs b/runtime/server.cjs index a154f68..dde23f2 100644 --- a/runtime/server.cjs +++ b/runtime/server.cjs @@ -133217,91 +133217,7 @@ var require_orders_service = __commonJS({ return this.mapOrder(order, true); } async submitOrder(userId, dto) { - const settings = await this.prisma.systemSettings.findUnique({ - where: { id: "default" }, - select: { - maxMeteragePerInvoice: true, - minOrderRollsPerProduct: true - } - }); - const minRolls = settings?.minOrderRollsPerProduct ?? 1; - const productIds = dto.items.map((i) => i.productId); - const uniqueProductIds = [...new Set(productIds)]; - if (uniqueProductIds.length !== productIds.length) { - throw new common_12.BadRequestException("\u0645\u062D\u0635\u0648\u0644 \u062A\u06A9\u0631\u0627\u0631\u06CC \u062F\u0631 \u0633\u0641\u0627\u0631\u0634 \u0645\u062C\u0627\u0632 \u0646\u06CC\u0633\u062A"); - } - const products = await this.prisma.product.findMany({ - where: { id: { in: uniqueProductIds } }, - include: { catalog: true, company: true } - }); - const productMap = new Map(products.map((p) => [p.id, p])); - for (const item of dto.items) { - const product = productMap.get(item.productId); - if (!product) { - throw new common_12.BadRequestException("\u06CC\u06A9 \u06CC\u0627 \u0686\u0646\u062F \u0645\u062D\u0635\u0648\u0644 \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A \u06CC\u0627 \u062F\u06CC\u06AF\u0631 \u062F\u0631 \u0633\u06CC\u0633\u062A\u0645 \u0648\u062C\u0648\u062F \u0646\u062F\u0627\u0631\u062F. \u0633\u0628\u062F \u0631\u0627 \u062E\u0627\u0644\u06CC \u06A9\u0646\u06CC\u062F \u0648 \u062F\u0648\u0628\u0627\u0631\u0647 \u0627\u0636\u0627\u0641\u0647 \u06A9\u0646\u06CC\u062F."); - } - if (!product.isActive) { - throw new common_12.BadRequestException(`\u0645\u062D\u0635\u0648\u0644 \xAB${product.name}\xBB \u063A\u06CC\u0631\u0641\u0639\u0627\u0644 \u0627\u0633\u062A \u0648 \u0642\u0627\u0628\u0644 \u0633\u0641\u0627\u0631\u0634 \u0646\u06CC\u0633\u062A`); - } - if (!product.company?.isActive) { - throw new common_12.BadRequestException(`\u0634\u0631\u06A9\u062A \u0645\u062D\u0635\u0648\u0644 \xAB${product.name}\xBB \u063A\u06CC\u0631\u0641\u0639\u0627\u0644 \u0627\u0633\u062A`); - } - } - const meterageByCompany = /* @__PURE__ */ new Map(); - for (const item of dto.items) { - const product = productMap.get(item.productId); - const current = meterageByCompany.get(product.companyId); - if (current) { - current.meterage += item.meterage; - } else { - meterageByCompany.set(product.companyId, { - name: product.company.name, - meterage: item.meterage, - max: product.company.maxMeteragePerInvoice - }); - } - } - for (const row of meterageByCompany.values()) { - const maxMeterageResolved = (0, meterage_1.resolveMaxMeterage)(row.max, settings?.maxMeteragePerInvoice); - if (row.meterage > maxMeterageResolved) { - throw new common_12.BadRequestException(`\u062D\u062F\u0627\u06A9\u062B\u0631 \u0645\u062A\u0631\u0627\u0698 \u0645\u062C\u0627\u0632 \u0641\u0627\u06A9\u062A\u0648\u0631 \u0628\u0631\u0627\u06CC \xAB${row.name}\xBB \u0628\u0631\u0627\u0628\u0631 ${maxMeterageResolved} \u0645\u062A\u0631 \u0627\u0633\u062A`); - } - } - let totalMeterage = 0; - const itemsData = dto.items.map((item) => { - const product = productMap.get(item.productId); - if (item.meterage < product.minMeterage) { - throw new common_12.BadRequestException(`\u062D\u062F\u0627\u0642\u0644 \u0645\u062A\u0631\u0627\u0698 \u0628\u0631\u0627\u06CC ${product.name} \u0628\u0631\u0627\u0628\u0631 ${product.minMeterage} \u0627\u0633\u062A`); - } - const rem = Math.round(item.meterage / product.orderMultiplier * 1e3) / 1e3; - if (Math.abs(rem - Math.round(rem)) > 1e-6) { - throw new common_12.BadRequestException(`\u0645\u062A\u0631\u0627\u0698 ${product.name} \u0628\u0627\u06CC\u062F \u0645\u0636\u0631\u0628 ${product.orderMultiplier} \u0628\u0627\u0634\u062F`); - } - const rollMeters = product.rollMeters && product.rollMeters > 0 ? product.rollMeters : product.minMeterage > 0 ? product.minMeterage : 50; - if (item.meterage / rollMeters + 1e-9 < minRolls) { - throw new common_12.BadRequestException(`\u062D\u062F\u0627\u0642\u0644 \u0633\u0641\u0627\u0631\u0634 \u0628\u0631\u0627\u06CC ${product.name} \u0628\u0631\u0627\u0628\u0631 ${minRolls} \u0637\u0627\u0642\u0647 \u0627\u0633\u062A`); - } - totalMeterage += item.meterage; - return { - productId: product.id, - catalogCode: product.catalog.code, - productName: product.name, - productCode: product.code, - color: product.color, - requestedMeterage: item.meterage, - notes: item.notes?.trim() || null, - companyId: product.companyId, - companyName: product.company.name - }; - }); - const companyNames = [ - ...new Set(itemsData.map((item) => item.companyName).filter(Boolean)) - ]; - const preferredCompanyId = itemsData.some((item) => item.companyId === dto.companyId) ? dto.companyId : itemsData[0].companyId; - const primaryCompany = products.find((p) => p.companyId === preferredCompanyId)?.company; - if (!primaryCompany) { - throw new common_12.NotFoundException("\u0634\u0631\u06A9\u062A \u0645\u062D\u0635\u0648\u0644\u0627\u062A \u06CC\u0627\u0641\u062A \u0646\u0634\u062F \u06CC\u0627 \u063A\u06CC\u0631\u0641\u0639\u0627\u0644 \u0627\u0633\u062A"); - } + const { itemsData, totalMeterage, primaryCompany, companyNames, uniqueProductIds } = await this.resolveOrderLines(dto.items, dto.companyId); const order = await this.createWithUniqueOrderNumber((orderNumber) => this.prisma.$transaction(async (tx) => { const created = await tx.order.create({ data: { @@ -133390,11 +133306,9 @@ var require_orders_service = __commonJS({ return this.findMyOrder(userId, id); } async adminCreate(adminId, dto) { - const company = await this.prisma.company.findUnique({ - where: { id: dto.companyId } - }); - if (!company) - throw new common_12.NotFoundException("\u0634\u0631\u06A9\u062A \u06CC\u0627\u0641\u062A \u0646\u0634\u062F"); + if (!dto.items?.length) { + throw new common_12.BadRequestException("\u062D\u062F\u0627\u0642\u0644 \u06CC\u06A9 \u0645\u062D\u0635\u0648\u0644 \u0628\u0631\u0627\u06CC \u062B\u0628\u062A \u067E\u06CC\u0634\u200C\u0633\u0641\u0627\u0631\u0634 \u0644\u0627\u0632\u0645 \u0627\u0633\u062A"); + } let userId = dto.customerId || dto.userId; if (userId) { const existing = await this.prisma.customer.findUnique({ @@ -133426,24 +133340,25 @@ var require_orders_service = __commonJS({ if (!userId) { throw new common_12.BadRequestException("\u0627\u0646\u062A\u062E\u0627\u0628 \u0645\u0634\u062A\u0631\u06CC \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A"); } - const totalMeterage = dto.meterage ?? 0; + const { itemsData, totalMeterage, primaryCompany, companyNames } = await this.resolveOrderLines(dto.items, dto.companyId); const ownerId = userId; const order = await this.createWithUniqueOrderNumber((orderNumber) => this.prisma.order.create({ data: { orderNumber, userId: ownerId, - companyId: company.id, - companyName: company.name, + companyId: primaryCompany.id, + companyName: companyNames.join("\u060C "), settlementType: null, status: client_1.OrderStatus.awaiting_review, totalMeterage, notes: dto.notes?.trim() || null, + items: { create: itemsData }, statusHistory: { create: { fromStatus: null, toStatus: client_1.OrderStatus.awaiting_review, changedBy: `admin:${adminId}`, - note: "\u0633\u0641\u0627\u0631\u0634 \u062A\u0648\u0633\u0637 \u0627\u062F\u0645\u06CC\u0646 \u062B\u0628\u062A \u0634\u062F" + note: "\u067E\u06CC\u0634\u200C\u0633\u0641\u0627\u0631\u0634 \u062A\u0648\u0633\u0637 \u0627\u062F\u0645\u06CC\u0646 \u062B\u0628\u062A \u0634\u062F" } } }, @@ -133453,36 +133368,88 @@ var require_orders_service = __commonJS({ } async updatePricing(id, adminId, dto) { const order = await this.getOrder(id); - const unknown = dto.items.find((item) => !order.items.some((i) => i.id === item.id)); - if (unknown) { + const existingIds = new Set(order.items.map((item) => item.id)); + const deletedIds = [...new Set(dto.deletedItemIds ?? [])]; + for (const delId of deletedIds) { + if (!existingIds.has(delId)) { + throw new common_12.BadRequestException(`\u0622\u06CC\u062A\u0645 ${delId} \u06CC\u0627\u0641\u062A \u0646\u0634\u062F`); + } + } + const updates = dto.items.filter((item) => item.id && existingIds.has(item.id)); + const creates = dto.items.filter((item) => item.productId && (!item.id || !existingIds.has(item.id))); + const unknown = dto.items.find((item) => item.id && !existingIds.has(item.id) && !item.productId); + if (unknown?.id) { throw new common_12.BadRequestException(`\u0622\u06CC\u062A\u0645 ${unknown.id} \u06CC\u0627\u0641\u062A \u0646\u0634\u062F`); } - await this.prisma.$transaction(async (tx) => { - let totalAmount = 0; - let totalMeterage = 0; - for (const existing of order.items) { - const item = dto.items.find((i) => i.id === existing.id); - const unitPrice = item?.unitPrice !== void 0 ? item.unitPrice : item?.supplyPrice !== void 0 ? item.supplyPrice : existing.unitPrice ?? 0; - const unitDiscount = item?.unitDiscount ?? existing.unitDiscount ?? 0; + const createdRows = []; + if (creates.length) { + const resolved = await this.resolveOrderLines(creates.map((item) => ({ + productId: item.productId, + meterage: item.requestedMeterage ?? item.confirmedMeterage ?? 0 + })), order.companyId); + for (const [index, row] of resolved.itemsData.entries()) { + const src = creates[index]; + const unitPrice = src.unitPrice ?? src.supplyPrice ?? 0; + const unitDiscount = src.unitDiscount ?? 0; if (unitDiscount > unitPrice) { throw new common_12.BadRequestException("\u062A\u062E\u0641\u06CC\u0641 \u0648\u0627\u062D\u062F \u0646\u0645\u06CC\u200C\u062A\u0648\u0627\u0646\u062F \u0627\u0632 \u0642\u06CC\u0645\u062A \u0648\u0627\u062D\u062F \u0628\u06CC\u0634\u062A\u0631 \u0628\u0627\u0634\u062F"); } - const confirmed = item?.confirmedMeterage ?? existing.confirmedMeterage ?? existing.requestedMeterage; - const lineTotal = roundMoney(Math.max(0, (unitPrice - unitDiscount) * confirmed)); - totalAmount += lineTotal; - totalMeterage += confirmed; + const confirmed = src.confirmedMeterage ?? row.requestedMeterage; + createdRows.push({ + ...row, + confirmedMeterage: confirmed, + unitPrice, + unitDiscount, + lineTotal: roundMoney(Math.max(0, (unitPrice - unitDiscount) * confirmed)) + }); + } + } + await this.prisma.$transaction(async (tx) => { + if (deletedIds.length) { + await tx.orderItem.deleteMany({ + where: { orderId: id, id: { in: deletedIds } } + }); + } + for (const existing of order.items) { + if (deletedIds.includes(existing.id)) + continue; + const item = updates.find((entry) => entry.id === existing.id); if (!item) continue; + const unitPrice = item.unitPrice !== void 0 ? item.unitPrice : item.supplyPrice !== void 0 ? item.supplyPrice : existing.unitPrice ?? 0; + const unitDiscount = item.unitDiscount ?? existing.unitDiscount ?? 0; + if (unitDiscount > unitPrice) { + throw new common_12.BadRequestException("\u062A\u062E\u0641\u06CC\u0641 \u0648\u0627\u062D\u062F \u0646\u0645\u06CC\u200C\u062A\u0648\u0627\u0646\u062F \u0627\u0632 \u0642\u06CC\u0645\u062A \u0648\u0627\u062D\u062F \u0628\u06CC\u0634\u062A\u0631 \u0628\u0627\u0634\u062F"); + } + const confirmed = item.confirmedMeterage ?? existing.confirmedMeterage ?? existing.requestedMeterage; await tx.orderItem.update({ where: { id: existing.id }, data: { confirmedMeterage: confirmed, unitPrice, unitDiscount, - lineTotal + lineTotal: roundMoney(Math.max(0, (unitPrice - unitDiscount) * confirmed)) } }); } + for (const row of createdRows) { + await tx.orderItem.create({ + data: { orderId: id, ...row } + }); + } + const remaining = await tx.orderItem.findMany({ where: { orderId: id } }); + if (!remaining.length) { + throw new common_12.BadRequestException("\u0633\u0641\u0627\u0631\u0634 \u0628\u0627\u06CC\u062F \u062D\u062F\u0627\u0642\u0644 \u06CC\u06A9 \u0642\u0644\u0645 \u062F\u0627\u0634\u062A\u0647 \u0628\u0627\u0634\u062F"); + } + let totalAmount = 0; + let totalMeterage = 0; + for (const existing of remaining) { + const unitPrice = existing.unitPrice ?? 0; + const unitDiscount = existing.unitDiscount ?? 0; + const confirmed = existing.confirmedMeterage ?? existing.requestedMeterage; + totalAmount += roundMoney(Math.max(0, (unitPrice - unitDiscount) * confirmed)); + totalMeterage += confirmed; + } totalAmount = roundMoney(totalAmount); const shippingCost = dto.shippingCost ?? order.shippingCost ?? 0; const totalDiscount = dto.totalDiscount ?? order.totalDiscount ?? 0; @@ -133672,6 +133639,100 @@ var require_orders_service = __commonJS({ }); return companies.length ? companies : order.company ? [order.company] : []; } + async resolveOrderLines(items, preferredCompanyId) { + const settings = await this.prisma.systemSettings.findUnique({ + where: { id: "default" }, + select: { + maxMeteragePerInvoice: true, + minOrderRollsPerProduct: true + } + }); + const minRolls = settings?.minOrderRollsPerProduct ?? 1; + const productIds = items.map((i) => i.productId); + const uniqueProductIds = [...new Set(productIds)]; + if (uniqueProductIds.length !== productIds.length) { + throw new common_12.BadRequestException("\u0645\u062D\u0635\u0648\u0644 \u062A\u06A9\u0631\u0627\u0631\u06CC \u062F\u0631 \u0633\u0641\u0627\u0631\u0634 \u0645\u062C\u0627\u0632 \u0646\u06CC\u0633\u062A"); + } + const products = await this.prisma.product.findMany({ + where: { id: { in: uniqueProductIds } }, + include: { catalog: true, company: true } + }); + const productMap = new Map(products.map((p) => [p.id, p])); + for (const item of items) { + const product = productMap.get(item.productId); + if (!product) { + throw new common_12.BadRequestException("\u06CC\u06A9 \u06CC\u0627 \u0686\u0646\u062F \u0645\u062D\u0635\u0648\u0644 \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A \u06CC\u0627 \u062F\u06CC\u06AF\u0631 \u062F\u0631 \u0633\u06CC\u0633\u062A\u0645 \u0648\u062C\u0648\u062F \u0646\u062F\u0627\u0631\u062F."); + } + if (!product.isActive) { + throw new common_12.BadRequestException(`\u0645\u062D\u0635\u0648\u0644 \xAB${product.name}\xBB \u063A\u06CC\u0631\u0641\u0639\u0627\u0644 \u0627\u0633\u062A \u0648 \u0642\u0627\u0628\u0644 \u0633\u0641\u0627\u0631\u0634 \u0646\u06CC\u0633\u062A`); + } + if (!product.company?.isActive) { + throw new common_12.BadRequestException(`\u0634\u0631\u06A9\u062A \u0645\u062D\u0635\u0648\u0644 \xAB${product.name}\xBB \u063A\u06CC\u0631\u0641\u0639\u0627\u0644 \u0627\u0633\u062A`); + } + } + const meterageByCompany = /* @__PURE__ */ new Map(); + for (const item of items) { + const product = productMap.get(item.productId); + const current = meterageByCompany.get(product.companyId); + if (current) { + current.meterage += item.meterage; + } else { + meterageByCompany.set(product.companyId, { + name: product.company.name, + meterage: item.meterage, + max: product.company.maxMeteragePerInvoice + }); + } + } + for (const row of meterageByCompany.values()) { + const maxMeterageResolved = (0, meterage_1.resolveMaxMeterage)(row.max, settings?.maxMeteragePerInvoice); + if (row.meterage > maxMeterageResolved) { + throw new common_12.BadRequestException(`\u062D\u062F\u0627\u06A9\u062B\u0631 \u0645\u062A\u0631\u0627\u0698 \u0645\u062C\u0627\u0632 \u0641\u0627\u06A9\u062A\u0648\u0631 \u0628\u0631\u0627\u06CC \xAB${row.name}\xBB \u0628\u0631\u0627\u0628\u0631 ${maxMeterageResolved} \u0645\u062A\u0631 \u0627\u0633\u062A`); + } + } + let totalMeterage = 0; + const itemsData = items.map((item) => { + const product = productMap.get(item.productId); + if (item.meterage < product.minMeterage) { + throw new common_12.BadRequestException(`\u062D\u062F\u0627\u0642\u0644 \u0645\u062A\u0631\u0627\u0698 \u0628\u0631\u0627\u06CC ${product.name} \u0628\u0631\u0627\u0628\u0631 ${product.minMeterage} \u0627\u0633\u062A`); + } + const rem = Math.round(item.meterage / product.orderMultiplier * 1e3) / 1e3; + if (Math.abs(rem - Math.round(rem)) > 1e-6) { + throw new common_12.BadRequestException(`\u0645\u062A\u0631\u0627\u0698 ${product.name} \u0628\u0627\u06CC\u062F \u0645\u0636\u0631\u0628 ${product.orderMultiplier} \u0628\u0627\u0634\u062F`); + } + const rollMeters = product.rollMeters && product.rollMeters > 0 ? product.rollMeters : product.minMeterage > 0 ? product.minMeterage : 50; + if (item.meterage / rollMeters + 1e-9 < minRolls) { + throw new common_12.BadRequestException(`\u062D\u062F\u0627\u0642\u0644 \u0633\u0641\u0627\u0631\u0634 \u0628\u0631\u0627\u06CC ${product.name} \u0628\u0631\u0627\u0628\u0631 ${minRolls} \u0637\u0627\u0642\u0647 \u0627\u0633\u062A`); + } + totalMeterage += item.meterage; + return { + productId: product.id, + catalogCode: product.catalog.code, + productName: product.name, + productCode: product.code, + color: product.color, + requestedMeterage: item.meterage, + notes: item.notes?.trim() || null, + companyId: product.companyId, + companyName: product.company.name + }; + }); + const companyNames = [ + ...new Set(itemsData.map((item) => item.companyName).filter(Boolean)) + ]; + const preferredId = itemsData.some((item) => item.companyId === preferredCompanyId) ? preferredCompanyId : itemsData[0].companyId; + const primaryCompany = products.find((p) => p.companyId === preferredId)?.company; + if (!primaryCompany) { + throw new common_12.NotFoundException("\u0634\u0631\u06A9\u062A \u0645\u062D\u0635\u0648\u0644\u0627\u062A \u06CC\u0627\u0641\u062A \u0646\u0634\u062F \u06CC\u0627 \u063A\u06CC\u0631\u0641\u0639\u0627\u0644 \u0627\u0633\u062A"); + } + return { + itemsData, + totalMeterage, + primaryCompany, + companyNames, + uniqueProductIds + }; + } async getOrder(id) { const order = await this.prisma.order.findUnique({ where: { id }, @@ -134041,6 +134102,7 @@ var require_order_dto = __commonJS({ __metadata2("design:type", String) ], AdminCreateOrderDto.prototype, "customerName", void 0); __decorate2([ + (0, class_validator_1.IsOptional)(), (0, class_validator_1.IsString)(), __metadata2("design:type", String) ], AdminCreateOrderDto.prototype, "companyId", void 0); @@ -134078,6 +134140,8 @@ var require_order_dto = __commonJS({ __name(this, "OrderItemPricingDto"); } id; + productId; + requestedMeterage; confirmedMeterage; unitPrice; supplyPrice; @@ -134085,9 +134149,22 @@ var require_order_dto = __commonJS({ }; exports2.OrderItemPricingDto = OrderItemPricingDto; __decorate2([ + (0, class_validator_1.IsOptional)(), (0, class_validator_1.IsString)(), __metadata2("design:type", String) ], OrderItemPricingDto.prototype, "id", void 0); + __decorate2([ + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata2("design:type", String) + ], OrderItemPricingDto.prototype, "productId", void 0); + __decorate2([ + (0, class_validator_1.IsOptional)(), + (0, class_transformer_1.Type)(() => Number), + (0, class_validator_1.IsNumber)(), + (0, class_validator_1.Min)(0), + __metadata2("design:type", Number) + ], OrderItemPricingDto.prototype, "requestedMeterage", void 0); __decorate2([ (0, class_validator_1.IsOptional)(), (0, class_transformer_1.Type)(() => Number), @@ -134124,6 +134201,7 @@ var require_order_dto = __commonJS({ shippingCost; totalDiscount; note; + deletedItemIds; }; exports2.UpdateOrderPricingDto = UpdateOrderPricingDto; __decorate2([ @@ -134151,6 +134229,12 @@ var require_order_dto = __commonJS({ (0, class_validator_1.IsString)(), __metadata2("design:type", String) ], UpdateOrderPricingDto.prototype, "note", void 0); + __decorate2([ + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsArray)(), + (0, class_validator_1.IsString)({ each: true }), + __metadata2("design:type", Array) + ], UpdateOrderPricingDto.prototype, "deletedItemIds", void 0); var UpdateOrderStatusDto = class { static { __name(this, "UpdateOrderStatusDto");