Rebuild order flow: settle after pricing, notifications, and invoice expiry.
Hide prices until admin sends the invoice, enforce status transitions, add customer/admin SMS+in-app notifications, schedule cash invoice reminders, and ship updated dist/prisma-client/deploy-bundle for git-pull deploys. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Binary file not shown.
Vendored
+4
@@ -10,6 +10,7 @@ exports.AppModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const core_1 = require("@nestjs/core");
|
||||
const config_1 = require("@nestjs/config");
|
||||
const schedule_1 = require("@nestjs/schedule");
|
||||
const prisma_module_1 = require("./prisma/prisma.module");
|
||||
const auth_module_1 = require("./auth/auth.module");
|
||||
const jwt_auth_guard_1 = require("./common/guards/jwt-auth.guard");
|
||||
@@ -25,6 +26,7 @@ const shipments_module_1 = require("./shipments/shipments.module");
|
||||
const dashboard_module_1 = require("./dashboard/dashboard.module");
|
||||
const settings_module_1 = require("./settings/settings.module");
|
||||
const carts_module_1 = require("./carts/carts.module");
|
||||
const notifications_module_1 = require("./notifications/notifications.module");
|
||||
let AppModule = class AppModule {
|
||||
};
|
||||
exports.AppModule = AppModule;
|
||||
@@ -32,6 +34,7 @@ exports.AppModule = AppModule = __decorate([
|
||||
(0, common_1.Module)({
|
||||
imports: [
|
||||
config_1.ConfigModule.forRoot({ isGlobal: true }),
|
||||
schedule_1.ScheduleModule.forRoot(),
|
||||
prisma_module_1.PrismaModule,
|
||||
auth_module_1.AuthModule,
|
||||
companies_module_1.CompaniesModule,
|
||||
@@ -46,6 +49,7 @@ exports.AppModule = AppModule = __decorate([
|
||||
dashboard_module_1.DashboardModule,
|
||||
settings_module_1.SettingsModule,
|
||||
carts_module_1.CartsModule,
|
||||
notifications_module_1.NotificationsModule,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,uCAAyC;AACzC,2CAA8C;AAC9C,0DAAsD;AACtD,oDAAgD;AAChD,mEAA8D;AAC9D,mEAA+D;AAC/D,gEAA4D;AAC5D,gEAA4D;AAC5D,mEAA+D;AAC/D,yEAAoE;AACpE,0DAAsD;AACtD,0DAAsD;AACtD,+EAA0E;AAC1E,mEAA+D;AAC/D,mEAA+D;AAC/D,gEAA4D;AAC5D,uDAAmD;AA2B5C,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IAzBrB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;YACxC,4BAAY;YACZ,wBAAU;YACV,kCAAe;YACf,gCAAc;YACd,gCAAc;YACd,kCAAe;YACf,qCAAgB;YAChB,4BAAY;YACZ,4BAAY;YACZ,yCAAkB;YAClB,kCAAe;YACf,kCAAe;YACf,gCAAc;YACd,0BAAW;SACZ;QACD,SAAS,EAAE;YACT;gBACE,OAAO,EAAE,gBAAS;gBAClB,QAAQ,EAAE,6BAAY;aACvB;SACF;KACF,CAAC;GACW,SAAS,CAAG"}
|
||||
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,uCAAyC;AACzC,2CAA8C;AAC9C,+CAAkD;AAClD,0DAAsD;AACtD,oDAAgD;AAChD,mEAA8D;AAC9D,mEAA+D;AAC/D,gEAA4D;AAC5D,gEAA4D;AAC5D,mEAA+D;AAC/D,yEAAoE;AACpE,0DAAsD;AACtD,0DAAsD;AACtD,+EAA0E;AAC1E,mEAA+D;AAC/D,mEAA+D;AAC/D,gEAA4D;AAC5D,uDAAmD;AACnD,+EAA2E;AA6BpE,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IA3BrB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;YACxC,yBAAc,CAAC,OAAO,EAAE;YACxB,4BAAY;YACZ,wBAAU;YACV,kCAAe;YACf,gCAAc;YACd,gCAAc;YACd,kCAAe;YACf,qCAAgB;YAChB,4BAAY;YACZ,4BAAY;YACZ,yCAAkB;YAClB,kCAAe;YACf,kCAAe;YACf,gCAAc;YACd,0BAAW;YACX,0CAAmB;SACpB;QACD,SAAS,EAAE;YACT;gBACE,OAAO,EAAE,gBAAS;gBAClB,QAAQ,EAAE,6BAAY;aACvB;SACF;KACF,CAAC;GACW,SAAS,CAAG"}
|
||||
Vendored
-6
@@ -18,7 +18,6 @@ export declare class CartsController {
|
||||
productCode: string;
|
||||
color: string;
|
||||
meterage: number;
|
||||
unitPrice: number | undefined;
|
||||
notes: string | undefined;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
@@ -42,7 +41,6 @@ export declare class CartsController {
|
||||
productCode: string;
|
||||
color: string;
|
||||
meterage: number;
|
||||
unitPrice: number | undefined;
|
||||
notes: string | undefined;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
@@ -66,7 +64,6 @@ export declare class CartsController {
|
||||
productCode: string;
|
||||
color: string;
|
||||
meterage: number;
|
||||
unitPrice: number | undefined;
|
||||
notes: string | undefined;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
@@ -90,7 +87,6 @@ export declare class CartsController {
|
||||
productCode: string;
|
||||
color: string;
|
||||
meterage: number;
|
||||
unitPrice: number | undefined;
|
||||
notes: string | undefined;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
@@ -114,7 +110,6 @@ export declare class CartsController {
|
||||
productCode: string;
|
||||
color: string;
|
||||
meterage: number;
|
||||
unitPrice: number | undefined;
|
||||
notes: string | undefined;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
@@ -138,7 +133,6 @@ export declare class CartsController {
|
||||
productCode: string;
|
||||
color: string;
|
||||
meterage: number;
|
||||
unitPrice: number | undefined;
|
||||
notes: string | undefined;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
|
||||
Vendored
-6
@@ -17,7 +17,6 @@ export declare class CartsService {
|
||||
productCode: string;
|
||||
color: string;
|
||||
meterage: number;
|
||||
unitPrice: number | undefined;
|
||||
notes: string | undefined;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
@@ -41,7 +40,6 @@ export declare class CartsService {
|
||||
productCode: string;
|
||||
color: string;
|
||||
meterage: number;
|
||||
unitPrice: number | undefined;
|
||||
notes: string | undefined;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
@@ -65,7 +63,6 @@ export declare class CartsService {
|
||||
productCode: string;
|
||||
color: string;
|
||||
meterage: number;
|
||||
unitPrice: number | undefined;
|
||||
notes: string | undefined;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
@@ -89,7 +86,6 @@ export declare class CartsService {
|
||||
productCode: string;
|
||||
color: string;
|
||||
meterage: number;
|
||||
unitPrice: number | undefined;
|
||||
notes: string | undefined;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
@@ -113,7 +109,6 @@ export declare class CartsService {
|
||||
productCode: string;
|
||||
color: string;
|
||||
meterage: number;
|
||||
unitPrice: number | undefined;
|
||||
notes: string | undefined;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
@@ -137,7 +132,6 @@ export declare class CartsService {
|
||||
productCode: string;
|
||||
color: string;
|
||||
meterage: number;
|
||||
unitPrice: number | undefined;
|
||||
notes: string | undefined;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
|
||||
Vendored
-3
@@ -47,7 +47,6 @@ let CartsService = class CartsService {
|
||||
data: {
|
||||
meterage: newMeterage,
|
||||
notes: dto.notes?.trim() || existing.notes,
|
||||
unitPrice: product.basePrice,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -61,7 +60,6 @@ let CartsService = class CartsService {
|
||||
productCode: product.code,
|
||||
color: product.color,
|
||||
meterage: dto.meterage,
|
||||
unitPrice: product.basePrice,
|
||||
notes: dto.notes?.trim() || null,
|
||||
companyId: product.companyId,
|
||||
companyName: product.company.name,
|
||||
@@ -168,7 +166,6 @@ let CartsService = class CartsService {
|
||||
productCode: item.productCode,
|
||||
color: item.color,
|
||||
meterage: item.meterage,
|
||||
unitPrice: item.unitPrice ?? undefined,
|
||||
notes: item.notes ?? undefined,
|
||||
companyId: item.companyId,
|
||||
companyName: item.companyName,
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+2
@@ -10,11 +10,13 @@ exports.CashPaymentsModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const cash_payments_controller_1 = require("./cash-payments.controller");
|
||||
const cash_payments_service_1 = require("./cash-payments.service");
|
||||
const notifications_module_1 = require("../notifications/notifications.module");
|
||||
let CashPaymentsModule = class CashPaymentsModule {
|
||||
};
|
||||
exports.CashPaymentsModule = CashPaymentsModule;
|
||||
exports.CashPaymentsModule = CashPaymentsModule = __decorate([
|
||||
(0, common_1.Module)({
|
||||
imports: [notifications_module_1.NotificationsModule],
|
||||
controllers: [cash_payments_controller_1.CashPaymentsController],
|
||||
providers: [cash_payments_service_1.CashPaymentsService],
|
||||
exports: [cash_payments_service_1.CashPaymentsService],
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"cash-payments.module.js","sourceRoot":"","sources":["../../src/cash-payments/cash-payments.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,yEAAoE;AACpE,mEAA8D;AAOvD,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;CAAG,CAAA;AAArB,gDAAkB;6BAAlB,kBAAkB;IAL9B,IAAA,eAAM,EAAC;QACN,WAAW,EAAE,CAAC,iDAAsB,CAAC;QACrC,SAAS,EAAE,CAAC,2CAAmB,CAAC;QAChC,OAAO,EAAE,CAAC,2CAAmB,CAAC;KAC/B,CAAC;GACW,kBAAkB,CAAG"}
|
||||
{"version":3,"file":"cash-payments.module.js","sourceRoot":"","sources":["../../src/cash-payments/cash-payments.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,yEAAoE;AACpE,mEAA8D;AAC9D,gFAA4E;AAQrE,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;CAAG,CAAA;AAArB,gDAAkB;6BAAlB,kBAAkB;IAN9B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,0CAAmB,CAAC;QAC9B,WAAW,EAAE,CAAC,iDAAsB,CAAC;QACrC,SAAS,EAAE,CAAC,2CAAmB,CAAC;QAChC,OAAO,EAAE,CAAC,2CAAmB,CAAC;KAC/B,CAAC;GACW,kBAAkB,CAAG"}
|
||||
+3
-1
@@ -1,9 +1,11 @@
|
||||
import { CashPaymentStatus } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { SubmitCashReceiptDto, UpdateCashPaymentStatusDto } from './dto/cash-payment.dto';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
export declare class CashPaymentsService {
|
||||
private readonly prisma;
|
||||
constructor(prisma: PrismaService);
|
||||
private readonly notifications;
|
||||
constructor(prisma: PrismaService, notifications: NotificationsService);
|
||||
submitReceipt(userId: string, orderId: string, dto: SubmitCashReceiptDto): Promise<{
|
||||
id: string;
|
||||
orderId: string;
|
||||
|
||||
+21
-8
@@ -14,12 +14,8 @@ const common_1 = require("@nestjs/common");
|
||||
const client_1 = require("@prisma/client");
|
||||
const prisma_service_1 = require("../prisma/prisma.service");
|
||||
const name_1 = require("../common/utils/name");
|
||||
const notifications_service_1 = require("../notifications/notifications.service");
|
||||
const RECEIPT_SUBMITTABLE_STATUSES = [
|
||||
client_1.OrderStatus.awaiting_review,
|
||||
client_1.OrderStatus.pricing,
|
||||
client_1.OrderStatus.needs_correction,
|
||||
client_1.OrderStatus.awaiting_settlement,
|
||||
client_1.OrderStatus.awaiting_settlement_fix,
|
||||
client_1.OrderStatus.cash_in_progress,
|
||||
client_1.OrderStatus.cash_invoice_issued,
|
||||
client_1.OrderStatus.awaiting_cash_payment,
|
||||
@@ -28,8 +24,10 @@ const RECEIPT_SUBMITTABLE_STATUSES = [
|
||||
];
|
||||
let CashPaymentsService = class CashPaymentsService {
|
||||
prisma;
|
||||
constructor(prisma) {
|
||||
notifications;
|
||||
constructor(prisma, notifications) {
|
||||
this.prisma = prisma;
|
||||
this.notifications = notifications;
|
||||
}
|
||||
async submitReceipt(userId, orderId, dto) {
|
||||
const order = await this.prisma.order.findUnique({
|
||||
@@ -98,6 +96,11 @@ let CashPaymentsService = class CashPaymentsService {
|
||||
});
|
||||
return saved;
|
||||
});
|
||||
this.notifications.dispatchAdmin({
|
||||
title: 'رسید پرداخت نقدی ثبت شد',
|
||||
body: `مشتری برای سفارش ${order.orderNumber} رسید به مبلغ ${Math.round(dto.amount).toLocaleString('fa-IR')} تومان ثبت کرد.`,
|
||||
link: `/orders/${orderId}`,
|
||||
});
|
||||
return this.mapReceipt(receipt, order.orderNumber, '');
|
||||
}
|
||||
async findAll(status) {
|
||||
@@ -135,7 +138,7 @@ let CashPaymentsService = class CashPaymentsService {
|
||||
nextStatus = client_1.OrderStatus.cash_confirmed;
|
||||
}
|
||||
else if (dto.status === client_1.CashPaymentStatus.rejected) {
|
||||
nextStatus = client_1.OrderStatus.needs_correction;
|
||||
nextStatus = client_1.OrderStatus.awaiting_cash_payment;
|
||||
}
|
||||
else if (dto.status === client_1.CashPaymentStatus.expired) {
|
||||
nextStatus = client_1.OrderStatus.expired;
|
||||
@@ -157,6 +160,15 @@ let CashPaymentsService = class CashPaymentsService {
|
||||
}
|
||||
return result;
|
||||
});
|
||||
if (dto.status === client_1.CashPaymentStatus.approved) {
|
||||
this.notifications.dispatchOrderEvent(receipt.orderId, 'receipt_approved');
|
||||
}
|
||||
else if (dto.status === client_1.CashPaymentStatus.rejected) {
|
||||
this.notifications.dispatchOrderEvent(receipt.orderId, 'receipt_rejected', { note: dto.note ?? null });
|
||||
}
|
||||
else if (dto.status === client_1.CashPaymentStatus.expired) {
|
||||
this.notifications.dispatchOrderEvent(receipt.orderId, 'cash_invoice_expired');
|
||||
}
|
||||
return this.mapReceipt(updated, updated.order.orderNumber, (0, name_1.joinFullName)(updated.order.user.firstName, updated.order.user.lastName));
|
||||
}
|
||||
mapReceipt(r, orderNumber, customerName) {
|
||||
@@ -185,6 +197,7 @@ let CashPaymentsService = class CashPaymentsService {
|
||||
exports.CashPaymentsService = CashPaymentsService;
|
||||
exports.CashPaymentsService = CashPaymentsService = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [prisma_service_1.PrismaService])
|
||||
__metadata("design:paramtypes", [prisma_service_1.PrismaService,
|
||||
notifications_service_1.NotificationsService])
|
||||
], CashPaymentsService);
|
||||
//# sourceMappingURL=cash-payments.service.js.map
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
@@ -10,11 +10,13 @@ exports.ChecksModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const checks_controller_1 = require("./checks.controller");
|
||||
const checks_service_1 = require("./checks.service");
|
||||
const notifications_module_1 = require("../notifications/notifications.module");
|
||||
let ChecksModule = class ChecksModule {
|
||||
};
|
||||
exports.ChecksModule = ChecksModule;
|
||||
exports.ChecksModule = ChecksModule = __decorate([
|
||||
(0, common_1.Module)({
|
||||
imports: [notifications_module_1.NotificationsModule],
|
||||
controllers: [checks_controller_1.ChecksController],
|
||||
providers: [checks_service_1.ChecksService],
|
||||
exports: [checks_service_1.ChecksService],
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"checks.module.js","sourceRoot":"","sources":["../../src/checks/checks.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2DAAuD;AACvD,qDAAiD;AAO1C,IAAM,YAAY,GAAlB,MAAM,YAAY;CAAG,CAAA;AAAf,oCAAY;uBAAZ,YAAY;IALxB,IAAA,eAAM,EAAC;QACN,WAAW,EAAE,CAAC,oCAAgB,CAAC;QAC/B,SAAS,EAAE,CAAC,8BAAa,CAAC;QAC1B,OAAO,EAAE,CAAC,8BAAa,CAAC;KACzB,CAAC;GACW,YAAY,CAAG"}
|
||||
{"version":3,"file":"checks.module.js","sourceRoot":"","sources":["../../src/checks/checks.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2DAAuD;AACvD,qDAAiD;AACjD,gFAA4E;AAQrE,IAAM,YAAY,GAAlB,MAAM,YAAY;CAAG,CAAA;AAAf,oCAAY;uBAAZ,YAAY;IANxB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,0CAAmB,CAAC;QAC9B,WAAW,EAAE,CAAC,oCAAgB,CAAC;QAC/B,SAAS,EAAE,CAAC,8BAAa,CAAC;QAC1B,OAAO,EAAE,CAAC,8BAAa,CAAC;KACzB,CAAC;GACW,YAAY,CAAG"}
|
||||
Vendored
+3
-1
@@ -1,8 +1,10 @@
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { UpdateCheckStatusDto } from './dto/check.dto';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
export declare class ChecksService {
|
||||
private readonly prisma;
|
||||
constructor(prisma: PrismaService);
|
||||
private readonly notifications;
|
||||
constructor(prisma: PrismaService, notifications: NotificationsService);
|
||||
findByOrder(orderId: string): Promise<{
|
||||
id: string;
|
||||
orderId: string;
|
||||
|
||||
Vendored
+17
-6
@@ -13,10 +13,13 @@ exports.ChecksService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const client_1 = require("@prisma/client");
|
||||
const prisma_service_1 = require("../prisma/prisma.service");
|
||||
const notifications_service_1 = require("../notifications/notifications.service");
|
||||
let ChecksService = class ChecksService {
|
||||
prisma;
|
||||
constructor(prisma) {
|
||||
notifications;
|
||||
constructor(prisma, notifications) {
|
||||
this.prisma = prisma;
|
||||
this.notifications = notifications;
|
||||
}
|
||||
async findByOrder(orderId) {
|
||||
const order = await this.prisma.order.findUnique({
|
||||
@@ -37,6 +40,7 @@ let ChecksService = class ChecksService {
|
||||
});
|
||||
if (!check)
|
||||
throw new common_1.NotFoundException('چک یافت نشد');
|
||||
let notifyStatus = null;
|
||||
const updated = await this.prisma.$transaction(async (tx) => {
|
||||
const result = await tx.check.update({
|
||||
where: { id },
|
||||
@@ -54,14 +58,11 @@ let ChecksService = class ChecksService {
|
||||
const anyRejected = siblings.some((c) => c.status === client_1.CheckStatus.rejected);
|
||||
let nextStatus = null;
|
||||
if (anyRejected) {
|
||||
nextStatus = client_1.OrderStatus.check_rejected;
|
||||
nextStatus = client_1.OrderStatus.check_needs_fix;
|
||||
}
|
||||
else if (allApproved) {
|
||||
nextStatus = client_1.OrderStatus.checks_approved;
|
||||
}
|
||||
else if (dto.status === client_1.CheckStatus.rejected) {
|
||||
nextStatus = client_1.OrderStatus.check_needs_fix;
|
||||
}
|
||||
if (nextStatus && nextStatus !== check.order.status) {
|
||||
await tx.order.update({
|
||||
where: { id: check.orderId },
|
||||
@@ -76,9 +77,18 @@ let ChecksService = class ChecksService {
|
||||
note: dto.note || `وضعیت چک به ${dto.status} تغییر کرد`,
|
||||
},
|
||||
});
|
||||
notifyStatus = nextStatus;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
if (notifyStatus === client_1.OrderStatus.checks_approved) {
|
||||
this.notifications.dispatchOrderEvent(check.orderId, 'checks_approved');
|
||||
}
|
||||
else if (notifyStatus === client_1.OrderStatus.check_needs_fix) {
|
||||
this.notifications.dispatchOrderEvent(check.orderId, 'checks_need_fix', {
|
||||
note: dto.note ?? null,
|
||||
});
|
||||
}
|
||||
return this.map(updated);
|
||||
}
|
||||
map(c) {
|
||||
@@ -108,6 +118,7 @@ let ChecksService = class ChecksService {
|
||||
exports.ChecksService = ChecksService;
|
||||
exports.ChecksService = ChecksService = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [prisma_service_1.PrismaService])
|
||||
__metadata("design:paramtypes", [prisma_service_1.PrismaService,
|
||||
notifications_service_1.NotificationsService])
|
||||
], ChecksService);
|
||||
//# sourceMappingURL=checks.service.js.map
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"checks.service.js","sourceRoot":"","sources":["../../src/checks/checks.service.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"checks.service.js","sourceRoot":"","sources":["../../src/checks/checks.service.ts"],"names":[],"mappings":"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"}
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
export declare const FALLBACK_MAX_METERAGE = 5000;
|
||||
export declare function resolveMaxMeterage(companyValue: number | null | undefined, settingsValue: number | null | undefined): number;
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.FALLBACK_MAX_METERAGE = void 0;
|
||||
exports.resolveMaxMeterage = resolveMaxMeterage;
|
||||
exports.FALLBACK_MAX_METERAGE = 5000;
|
||||
function resolveMaxMeterage(companyValue, settingsValue) {
|
||||
if (companyValue != null && companyValue > 0)
|
||||
return companyValue;
|
||||
if (settingsValue != null && settingsValue > 0)
|
||||
return settingsValue;
|
||||
return exports.FALLBACK_MAX_METERAGE;
|
||||
}
|
||||
//# sourceMappingURL=meterage.js.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"meterage.js","sourceRoot":"","sources":["../../../src/common/utils/meterage.ts"],"names":[],"mappings":";;;AAMA,gDAOC;AAZY,QAAA,qBAAqB,GAAG,IAAI,CAAC;AAK1C,SAAgB,kBAAkB,CAChC,YAAuC,EACvC,aAAwC;IAExC,IAAI,YAAY,IAAI,IAAI,IAAI,YAAY,GAAG,CAAC;QAAE,OAAO,YAAY,CAAC;IAClE,IAAI,aAAa,IAAI,IAAI,IAAI,aAAa,GAAG,CAAC;QAAE,OAAO,aAAa,CAAC;IACrE,OAAO,6BAAqB,CAAC;AAC/B,CAAC"}
|
||||
Vendored
+10
-8
@@ -7,22 +7,24 @@ const client_1 = require("@prisma/client");
|
||||
exports.ADMIN_STATUS_MAP = {
|
||||
pending_review: [
|
||||
client_1.OrderStatus.cart,
|
||||
client_1.OrderStatus.awaiting_settlement,
|
||||
client_1.OrderStatus.cash_in_progress,
|
||||
client_1.OrderStatus.check_in_progress,
|
||||
client_1.OrderStatus.submitted,
|
||||
client_1.OrderStatus.awaiting_review,
|
||||
client_1.OrderStatus.pricing,
|
||||
],
|
||||
awaiting_customer: [
|
||||
client_1.OrderStatus.awaiting_settlement,
|
||||
client_1.OrderStatus.awaiting_settlement_fix,
|
||||
client_1.OrderStatus.check_in_progress,
|
||||
client_1.OrderStatus.cash_in_progress,
|
||||
client_1.OrderStatus.cash_invoice_issued,
|
||||
client_1.OrderStatus.awaiting_cash_payment,
|
||||
],
|
||||
pending_check: [client_1.OrderStatus.awaiting_check_review],
|
||||
pending_payment: [
|
||||
client_1.OrderStatus.cash_payment_submitted,
|
||||
client_1.OrderStatus.awaiting_cash_confirmation,
|
||||
],
|
||||
pending_check: [client_1.OrderStatus.awaiting_check_review],
|
||||
needs_correction: [
|
||||
client_1.OrderStatus.needs_correction,
|
||||
client_1.OrderStatus.awaiting_settlement_fix,
|
||||
],
|
||||
needs_correction: [client_1.OrderStatus.needs_correction],
|
||||
check_correction: [client_1.OrderStatus.check_needs_fix, client_1.OrderStatus.check_rejected],
|
||||
final_approval: [
|
||||
client_1.OrderStatus.final_approved,
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"order-status.js","sourceRoot":"","sources":["../../../src/common/utils/order-status.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"order-status.js","sourceRoot":"","sources":["../../../src/common/utils/order-status.ts"],"names":[],"mappings":"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"}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { OrderStatus } from '@prisma/client';
|
||||
export declare const ORDER_TRANSITIONS: Record<OrderStatus, OrderStatus[]>;
|
||||
export declare function isFinalStatus(status: OrderStatus): boolean;
|
||||
export declare function allowedTransitions(from: OrderStatus): OrderStatus[];
|
||||
export declare function canTransition(from: OrderStatus, to: OrderStatus): boolean;
|
||||
export declare function assertTransition(from: OrderStatus, to: OrderStatus): void;
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ORDER_TRANSITIONS = void 0;
|
||||
exports.isFinalStatus = isFinalStatus;
|
||||
exports.allowedTransitions = allowedTransitions;
|
||||
exports.canTransition = canTransition;
|
||||
exports.assertTransition = assertTransition;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const client_1 = require("@prisma/client");
|
||||
const order_status_1 = require("./order-status");
|
||||
exports.ORDER_TRANSITIONS = {
|
||||
awaiting_review: [client_1.OrderStatus.pricing],
|
||||
pricing: [client_1.OrderStatus.awaiting_settlement],
|
||||
awaiting_settlement: [
|
||||
client_1.OrderStatus.awaiting_cash_payment,
|
||||
client_1.OrderStatus.check_in_progress,
|
||||
client_1.OrderStatus.pricing,
|
||||
],
|
||||
awaiting_cash_payment: [
|
||||
client_1.OrderStatus.awaiting_cash_confirmation,
|
||||
client_1.OrderStatus.expired,
|
||||
],
|
||||
awaiting_cash_confirmation: [
|
||||
client_1.OrderStatus.cash_confirmed,
|
||||
client_1.OrderStatus.awaiting_cash_payment,
|
||||
],
|
||||
cash_confirmed: [client_1.OrderStatus.final_approved],
|
||||
check_in_progress: [client_1.OrderStatus.awaiting_check_review],
|
||||
awaiting_check_review: [
|
||||
client_1.OrderStatus.checks_approved,
|
||||
client_1.OrderStatus.check_needs_fix,
|
||||
],
|
||||
check_needs_fix: [client_1.OrderStatus.awaiting_check_review],
|
||||
checks_approved: [client_1.OrderStatus.final_approved],
|
||||
final_approved: [client_1.OrderStatus.preparing],
|
||||
preparing: [client_1.OrderStatus.ready_to_ship],
|
||||
ready_to_ship: [client_1.OrderStatus.shipped],
|
||||
shipped: [client_1.OrderStatus.delivered],
|
||||
delivered: [],
|
||||
cancelled: [],
|
||||
rejected: [],
|
||||
expired: [],
|
||||
cart: [client_1.OrderStatus.awaiting_review],
|
||||
submitted: [client_1.OrderStatus.awaiting_review],
|
||||
needs_correction: [client_1.OrderStatus.awaiting_review, client_1.OrderStatus.pricing],
|
||||
awaiting_settlement_fix: [client_1.OrderStatus.awaiting_settlement],
|
||||
cash_in_progress: [client_1.OrderStatus.awaiting_cash_payment],
|
||||
cash_invoice_issued: [client_1.OrderStatus.awaiting_cash_payment],
|
||||
cash_payment_submitted: [client_1.OrderStatus.awaiting_cash_confirmation],
|
||||
check_rejected: [client_1.OrderStatus.check_needs_fix],
|
||||
};
|
||||
const ADMIN_TERMINAL_TRANSITIONS = [
|
||||
client_1.OrderStatus.cancelled,
|
||||
client_1.OrderStatus.rejected,
|
||||
];
|
||||
const FINAL_STATUSES = [
|
||||
client_1.OrderStatus.delivered,
|
||||
client_1.OrderStatus.cancelled,
|
||||
client_1.OrderStatus.rejected,
|
||||
client_1.OrderStatus.expired,
|
||||
];
|
||||
function isFinalStatus(status) {
|
||||
return FINAL_STATUSES.includes(status);
|
||||
}
|
||||
function allowedTransitions(from) {
|
||||
const next = [...(exports.ORDER_TRANSITIONS[from] ?? [])];
|
||||
if (!isFinalStatus(from)) {
|
||||
for (const terminal of ADMIN_TERMINAL_TRANSITIONS) {
|
||||
if (!next.includes(terminal))
|
||||
next.push(terminal);
|
||||
}
|
||||
}
|
||||
return next;
|
||||
}
|
||||
function canTransition(from, to) {
|
||||
return allowedTransitions(from).includes(to);
|
||||
}
|
||||
function assertTransition(from, to) {
|
||||
if (canTransition(from, to))
|
||||
return;
|
||||
throw new common_1.BadRequestException(`تغییر وضعیت از «${order_status_1.ORDER_STATUS_LABELS[from]}» به «${order_status_1.ORDER_STATUS_LABELS[to]}» مجاز نیست`);
|
||||
}
|
||||
//# sourceMappingURL=order-transitions.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"order-transitions.js","sourceRoot":"","sources":["../../../src/common/utils/order-transitions.ts"],"names":[],"mappings":"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"}
|
||||
+6
@@ -13,6 +13,7 @@ export declare class CompaniesController {
|
||||
isActive: boolean;
|
||||
displayOrder: number;
|
||||
maxMeteragePerInvoice: number;
|
||||
ownMaxMeteragePerInvoice: number | undefined;
|
||||
cashInvoiceValidityHours: number;
|
||||
cashInvoiceValidUntilHour: number | undefined;
|
||||
catalogCount: number;
|
||||
@@ -31,6 +32,7 @@ export declare class CompaniesController {
|
||||
isActive: boolean;
|
||||
displayOrder: number;
|
||||
maxMeteragePerInvoice: number;
|
||||
ownMaxMeteragePerInvoice: number | undefined;
|
||||
cashInvoiceValidityHours: number;
|
||||
cashInvoiceValidUntilHour: number | undefined;
|
||||
catalogCount: number;
|
||||
@@ -63,6 +65,7 @@ export declare class CompaniesController {
|
||||
isActive: boolean;
|
||||
displayOrder: number;
|
||||
maxMeteragePerInvoice: number;
|
||||
ownMaxMeteragePerInvoice: number | undefined;
|
||||
cashInvoiceValidityHours: number;
|
||||
cashInvoiceValidUntilHour: number | undefined;
|
||||
catalogCount: number;
|
||||
@@ -81,6 +84,7 @@ export declare class CompaniesController {
|
||||
isActive: boolean;
|
||||
displayOrder: number;
|
||||
maxMeteragePerInvoice: number;
|
||||
ownMaxMeteragePerInvoice: number | undefined;
|
||||
cashInvoiceValidityHours: number;
|
||||
cashInvoiceValidUntilHour: number | undefined;
|
||||
catalogCount: number;
|
||||
@@ -99,6 +103,7 @@ export declare class CompaniesController {
|
||||
isActive: boolean;
|
||||
displayOrder: number;
|
||||
maxMeteragePerInvoice: number;
|
||||
ownMaxMeteragePerInvoice: number | undefined;
|
||||
cashInvoiceValidityHours: number;
|
||||
cashInvoiceValidUntilHour: number | undefined;
|
||||
catalogCount: number;
|
||||
@@ -117,6 +122,7 @@ export declare class CompaniesController {
|
||||
isActive: boolean;
|
||||
displayOrder: number;
|
||||
maxMeteragePerInvoice: number;
|
||||
ownMaxMeteragePerInvoice: number | undefined;
|
||||
cashInvoiceValidityHours: number;
|
||||
cashInvoiceValidUntilHour: number | undefined;
|
||||
catalogCount: number;
|
||||
|
||||
+6
@@ -13,6 +13,7 @@ export declare class CompaniesService {
|
||||
isActive: boolean;
|
||||
displayOrder: number;
|
||||
maxMeteragePerInvoice: number;
|
||||
ownMaxMeteragePerInvoice: number | undefined;
|
||||
cashInvoiceValidityHours: number;
|
||||
cashInvoiceValidUntilHour: number | undefined;
|
||||
catalogCount: number;
|
||||
@@ -45,6 +46,7 @@ export declare class CompaniesService {
|
||||
isActive: boolean;
|
||||
displayOrder: number;
|
||||
maxMeteragePerInvoice: number;
|
||||
ownMaxMeteragePerInvoice: number | undefined;
|
||||
cashInvoiceValidityHours: number;
|
||||
cashInvoiceValidUntilHour: number | undefined;
|
||||
catalogCount: number;
|
||||
@@ -53,6 +55,7 @@ export declare class CompaniesService {
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}>;
|
||||
private settingsMaxMeterage;
|
||||
create(dto: CreateCompanyDto): Promise<{
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -63,6 +66,7 @@ export declare class CompaniesService {
|
||||
isActive: boolean;
|
||||
displayOrder: number;
|
||||
maxMeteragePerInvoice: number;
|
||||
ownMaxMeteragePerInvoice: number | undefined;
|
||||
cashInvoiceValidityHours: number;
|
||||
cashInvoiceValidUntilHour: number | undefined;
|
||||
catalogCount: number;
|
||||
@@ -81,6 +85,7 @@ export declare class CompaniesService {
|
||||
isActive: boolean;
|
||||
displayOrder: number;
|
||||
maxMeteragePerInvoice: number;
|
||||
ownMaxMeteragePerInvoice: number | undefined;
|
||||
cashInvoiceValidityHours: number;
|
||||
cashInvoiceValidUntilHour: number | undefined;
|
||||
catalogCount: number;
|
||||
@@ -99,6 +104,7 @@ export declare class CompaniesService {
|
||||
isActive: boolean;
|
||||
displayOrder: number;
|
||||
maxMeteragePerInvoice: number;
|
||||
ownMaxMeteragePerInvoice: number | undefined;
|
||||
cashInvoiceValidityHours: number;
|
||||
cashInvoiceValidUntilHour: number | undefined;
|
||||
catalogCount: number;
|
||||
|
||||
Vendored
+36
-21
@@ -13,40 +13,54 @@ exports.CompaniesService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const client_1 = require("@prisma/client");
|
||||
const prisma_service_1 = require("../prisma/prisma.service");
|
||||
const meterage_1 = require("../common/utils/meterage");
|
||||
let CompaniesService = class CompaniesService {
|
||||
prisma;
|
||||
constructor(prisma) {
|
||||
this.prisma = prisma;
|
||||
}
|
||||
async findAll(activeOnly = false) {
|
||||
const companies = await this.prisma.company.findMany({
|
||||
where: activeOnly ? { isActive: true } : undefined,
|
||||
orderBy: [{ displayOrder: 'asc' }, { name: 'asc' }],
|
||||
include: {
|
||||
_count: {
|
||||
select: { catalogs: true, products: true, bankAccounts: true },
|
||||
const [companies, fallbackMeterage] = await Promise.all([
|
||||
this.prisma.company.findMany({
|
||||
where: activeOnly ? { isActive: true } : undefined,
|
||||
orderBy: [{ displayOrder: 'asc' }, { name: 'asc' }],
|
||||
include: {
|
||||
_count: {
|
||||
select: { catalogs: true, products: true, bankAccounts: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
return companies.map((c) => this.mapCompany(c));
|
||||
}),
|
||||
this.settingsMaxMeterage(),
|
||||
]);
|
||||
return companies.map((c) => this.mapCompany(c, fallbackMeterage));
|
||||
}
|
||||
async findOne(id) {
|
||||
const company = await this.prisma.company.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
bankAccounts: { orderBy: { displayOrder: 'asc' } },
|
||||
_count: {
|
||||
select: { catalogs: true, products: true, bankAccounts: true },
|
||||
const [company, fallbackMeterage] = await Promise.all([
|
||||
this.prisma.company.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
bankAccounts: { orderBy: { displayOrder: 'asc' } },
|
||||
_count: {
|
||||
select: { catalogs: true, products: true, bankAccounts: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
this.settingsMaxMeterage(),
|
||||
]);
|
||||
if (!company)
|
||||
throw new common_1.NotFoundException('شرکت یافت نشد');
|
||||
return {
|
||||
...this.mapCompany(company),
|
||||
...this.mapCompany(company, fallbackMeterage),
|
||||
bankAccounts: company.bankAccounts.map((b) => this.mapBankAccount(b)),
|
||||
};
|
||||
}
|
||||
async settingsMaxMeterage() {
|
||||
const settings = await this.prisma.systemSettings.findUnique({
|
||||
where: { id: 'default' },
|
||||
select: { maxMeteragePerInvoice: true },
|
||||
});
|
||||
return settings?.maxMeteragePerInvoice ?? null;
|
||||
}
|
||||
async create(dto) {
|
||||
try {
|
||||
const company = await this.prisma.company.create({
|
||||
@@ -56,7 +70,7 @@ let CompaniesService = class CompaniesService {
|
||||
description: dto.description?.trim() || null,
|
||||
logoUrl: dto.logoUrl?.trim() || null,
|
||||
displayOrder: dto.displayOrder ?? 0,
|
||||
maxMeteragePerInvoice: dto.maxMeteragePerInvoice ?? 5000,
|
||||
maxMeteragePerInvoice: dto.maxMeteragePerInvoice ?? null,
|
||||
cashInvoiceValidityHours: dto.cashInvoiceValidityHours ?? 8,
|
||||
cashInvoiceValidUntilHour: dto.cashInvoiceValidUntilHour ?? null,
|
||||
isActive: dto.isActive ?? true,
|
||||
@@ -186,7 +200,7 @@ let CompaniesService = class CompaniesService {
|
||||
throw new common_1.NotFoundException('حساب بانکی یافت نشد');
|
||||
return account;
|
||||
}
|
||||
mapCompany(company) {
|
||||
mapCompany(company, fallbackMaxMeterage = null) {
|
||||
return {
|
||||
id: company.id,
|
||||
name: company.name,
|
||||
@@ -196,7 +210,8 @@ let CompaniesService = class CompaniesService {
|
||||
logo: company.logoUrl ?? undefined,
|
||||
isActive: company.isActive,
|
||||
displayOrder: company.displayOrder,
|
||||
maxMeteragePerInvoice: company.maxMeteragePerInvoice,
|
||||
maxMeteragePerInvoice: (0, meterage_1.resolveMaxMeterage)(company.maxMeteragePerInvoice, fallbackMaxMeterage),
|
||||
ownMaxMeteragePerInvoice: company.maxMeteragePerInvoice ?? undefined,
|
||||
cashInvoiceValidityHours: company.cashInvoiceValidityHours,
|
||||
cashInvoiceValidUntilHour: company.cashInvoiceValidUntilHour ?? undefined,
|
||||
catalogCount: company._count?.catalogs ?? 0,
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+6
-4
@@ -18,17 +18,19 @@ export declare class DashboardController {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -50,8 +52,8 @@ export declare class DashboardController {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
|
||||
+6
-4
@@ -20,17 +20,19 @@ export declare class DashboardService {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -52,8 +54,8 @@ export declare class DashboardService {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { NotificationTone, OrderStatus } from '@prisma/client';
|
||||
export type CustomerEvent = 'order_submitted' | 'invoice_priced' | 'checks_approved' | 'checks_need_fix' | 'receipt_approved' | 'receipt_rejected' | 'cash_invoice_reminder' | 'cash_invoice_expired' | 'order_preparing' | 'order_ready_to_ship' | 'order_shipped' | 'order_delivered' | 'order_cancelled';
|
||||
type SmsSwitch = 'smsOrderRegistered' | 'smsPricingReady' | 'smsPaymentReminder' | 'smsCheckReview' | 'smsShippingUpdate' | null;
|
||||
export type CustomerEventTemplate = {
|
||||
title: string;
|
||||
body: (ctx: EventContext) => string;
|
||||
sms?: (ctx: EventContext) => string;
|
||||
tone: NotificationTone;
|
||||
smsSwitch: SmsSwitch;
|
||||
};
|
||||
export type EventContext = {
|
||||
orderNumber: string;
|
||||
appName: string;
|
||||
amount?: number | null;
|
||||
note?: string | null;
|
||||
deadline?: string | null;
|
||||
carrier?: string | null;
|
||||
trackingNumber?: string | null;
|
||||
};
|
||||
export declare function eventForStatus(status: OrderStatus): CustomerEvent | null;
|
||||
export declare const CUSTOMER_EVENTS: Record<CustomerEvent, CustomerEventTemplate>;
|
||||
export {};
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CUSTOMER_EVENTS = void 0;
|
||||
exports.eventForStatus = eventForStatus;
|
||||
const client_1 = require("@prisma/client");
|
||||
function money(amount) {
|
||||
if (!amount)
|
||||
return '—';
|
||||
return `${Math.round(amount).toLocaleString('fa-IR')} تومان`;
|
||||
}
|
||||
function eventForStatus(status) {
|
||||
switch (status) {
|
||||
case client_1.OrderStatus.awaiting_settlement:
|
||||
return 'invoice_priced';
|
||||
case client_1.OrderStatus.preparing:
|
||||
case client_1.OrderStatus.final_approved:
|
||||
return 'order_preparing';
|
||||
case client_1.OrderStatus.ready_to_ship:
|
||||
return 'order_ready_to_ship';
|
||||
case client_1.OrderStatus.shipped:
|
||||
return 'order_shipped';
|
||||
case client_1.OrderStatus.delivered:
|
||||
return 'order_delivered';
|
||||
case client_1.OrderStatus.cancelled:
|
||||
case client_1.OrderStatus.rejected:
|
||||
return 'order_cancelled';
|
||||
case client_1.OrderStatus.expired:
|
||||
return 'cash_invoice_expired';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
exports.CUSTOMER_EVENTS = {
|
||||
order_submitted: {
|
||||
title: 'سفارش شما ثبت شد',
|
||||
body: (c) => `سفارش ${c.orderNumber} ثبت شد و برای بررسی و قیمتگذاری به کارشناسان ما رسید.`,
|
||||
sms: (c) => `سفارش ${c.orderNumber} در ${c.appName} ثبت شد. پس از قیمتگذاری، فاکتور برای شما ارسال میشود.`,
|
||||
tone: client_1.NotificationTone.info,
|
||||
smsSwitch: 'smsOrderRegistered',
|
||||
},
|
||||
invoice_priced: {
|
||||
title: 'فاکتور سفارش شما صادر شد',
|
||||
body: (c) => `مبلغ فاکتور سفارش ${c.orderNumber} ${money(c.amount)} است. برای ادامه، روش تسویه را انتخاب کنید.`,
|
||||
sms: (c) => `فاکتور سفارش ${c.orderNumber} به مبلغ ${money(c.amount)} صادر شد. برای انتخاب روش تسویه به پنل ${c.appName} مراجعه کنید.`,
|
||||
tone: client_1.NotificationTone.success,
|
||||
smsSwitch: 'smsPricingReady',
|
||||
},
|
||||
checks_approved: {
|
||||
title: 'چکهای شما تایید شد',
|
||||
body: (c) => `همه چکهای سفارش ${c.orderNumber} تایید شدند.`,
|
||||
sms: (c) => `چکهای سفارش ${c.orderNumber} تایید شد و سفارش به مرحله آمادهسازی میرود.`,
|
||||
tone: client_1.NotificationTone.success,
|
||||
smsSwitch: 'smsCheckReview',
|
||||
},
|
||||
checks_need_fix: {
|
||||
title: 'چک شما نیاز به اصلاح دارد',
|
||||
body: (c) => `یک یا چند چک سفارش ${c.orderNumber} تایید نشد${c.note ? `: ${c.note}` : '.'} لطفاً چکها را اصلاح و مجدداً ارسال کنید.`,
|
||||
sms: (c) => `چک سفارش ${c.orderNumber} تایید نشد. برای اصلاح چکها به پنل ${c.appName} مراجعه کنید.`,
|
||||
tone: client_1.NotificationTone.danger,
|
||||
smsSwitch: 'smsCheckReview',
|
||||
},
|
||||
receipt_approved: {
|
||||
title: 'پرداخت شما تایید شد',
|
||||
body: (c) => `رسید پرداخت سفارش ${c.orderNumber} تایید شد.`,
|
||||
sms: (c) => `رسید پرداخت سفارش ${c.orderNumber} تایید شد. سفارش شما در حال آمادهسازی است.`,
|
||||
tone: client_1.NotificationTone.success,
|
||||
smsSwitch: 'smsPaymentReminder',
|
||||
},
|
||||
receipt_rejected: {
|
||||
title: 'رسید پرداخت تایید نشد',
|
||||
body: (c) => `رسید پرداخت سفارش ${c.orderNumber} تایید نشد${c.note ? `: ${c.note}` : '.'} لطفاً رسید صحیح را ثبت کنید.`,
|
||||
sms: (c) => `رسید پرداخت سفارش ${c.orderNumber} تایید نشد. لطفاً در پنل ${c.appName} رسید را اصلاح کنید.`,
|
||||
tone: client_1.NotificationTone.danger,
|
||||
smsSwitch: 'smsPaymentReminder',
|
||||
},
|
||||
cash_invoice_reminder: {
|
||||
title: 'یادآوری مهلت پرداخت',
|
||||
body: (c) => `مهلت پرداخت فاکتور سفارش ${c.orderNumber} به پایان نزدیک است. مبلغ قابل پرداخت ${money(c.amount)}.`,
|
||||
sms: (c) => `مهلت پرداخت فاکتور سفارش ${c.orderNumber} در حال پایان است. مبلغ ${money(c.amount)}.`,
|
||||
tone: client_1.NotificationTone.warning,
|
||||
smsSwitch: 'smsPaymentReminder',
|
||||
},
|
||||
cash_invoice_expired: {
|
||||
title: 'مهلت پرداخت فاکتور تمام شد',
|
||||
body: (c) => `مهلت پرداخت فاکتور سفارش ${c.orderNumber} به پایان رسید. برای ثبت سفارش تازه با پشتیبانی تماس بگیرید.`,
|
||||
sms: (c) => `مهلت پرداخت فاکتور سفارش ${c.orderNumber} به پایان رسید.`,
|
||||
tone: client_1.NotificationTone.danger,
|
||||
smsSwitch: 'smsPaymentReminder',
|
||||
},
|
||||
order_preparing: {
|
||||
title: 'سفارش در حال آمادهسازی',
|
||||
body: (c) => `سفارش ${c.orderNumber} تایید نهایی شد و در حال آمادهسازی است.`,
|
||||
tone: client_1.NotificationTone.info,
|
||||
smsSwitch: null,
|
||||
},
|
||||
order_ready_to_ship: {
|
||||
title: 'سفارش آماده ارسال است',
|
||||
body: (c) => `سفارش ${c.orderNumber} آماده ارسال شد.`,
|
||||
sms: (c) => `سفارش ${c.orderNumber} آماده ارسال شد.`,
|
||||
tone: client_1.NotificationTone.info,
|
||||
smsSwitch: 'smsShippingUpdate',
|
||||
},
|
||||
order_shipped: {
|
||||
title: 'سفارش ارسال شد',
|
||||
body: (c) => `سفارش ${c.orderNumber} ارسال شد${c.carrier ? ` (${c.carrier})` : ''}${c.trackingNumber ? ` با کد رهگیری ${c.trackingNumber}` : ''}.`,
|
||||
sms: (c) => `سفارش ${c.orderNumber} ارسال شد${c.trackingNumber ? `. کد رهگیری: ${c.trackingNumber}` : '.'}`,
|
||||
tone: client_1.NotificationTone.success,
|
||||
smsSwitch: 'smsShippingUpdate',
|
||||
},
|
||||
order_delivered: {
|
||||
title: 'سفارش تحویل شد',
|
||||
body: (c) => `سفارش ${c.orderNumber} تحویل داده شد. از خرید شما سپاسگزاریم.`,
|
||||
sms: (c) => `سفارش ${c.orderNumber} تحویل داده شد. از خرید شما سپاسگزاریم.`,
|
||||
tone: client_1.NotificationTone.success,
|
||||
smsSwitch: 'smsShippingUpdate',
|
||||
},
|
||||
order_cancelled: {
|
||||
title: 'سفارش لغو شد',
|
||||
body: (c) => `سفارش ${c.orderNumber} لغو شد${c.note ? `: ${c.note}` : '.'} برای اطلاعات بیشتر با پشتیبانی تماس بگیرید.`,
|
||||
sms: (c) => `سفارش ${c.orderNumber} لغو شد.`,
|
||||
tone: client_1.NotificationTone.danger,
|
||||
smsSwitch: null,
|
||||
},
|
||||
};
|
||||
//# sourceMappingURL=notification-events.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"notification-events.js","sourceRoot":"","sources":["../../src/notifications/notification-events.ts"],"names":[],"mappings":"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"}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { NotificationsService } from './notifications.service';
|
||||
import type { AuthPrincipal } from '../common/decorators/current-user.decorator';
|
||||
export declare class CustomerNotificationsController {
|
||||
private readonly notifications;
|
||||
constructor(notifications: NotificationsService);
|
||||
list(user: AuthPrincipal): Promise<{
|
||||
id: string;
|
||||
orderId: string | undefined;
|
||||
event: string;
|
||||
title: string;
|
||||
body: string;
|
||||
tone: import("@prisma/client").$Enums.NotificationTone;
|
||||
link: string | undefined;
|
||||
isRead: boolean;
|
||||
sentAt: string;
|
||||
}[]>;
|
||||
unreadCount(user: AuthPrincipal): Promise<{
|
||||
count: number;
|
||||
}>;
|
||||
markAllRead(user: AuthPrincipal): Promise<{
|
||||
count: number;
|
||||
}>;
|
||||
markRead(user: AuthPrincipal, id: string): Promise<{
|
||||
count: number;
|
||||
}>;
|
||||
}
|
||||
export declare class AdminNotificationsController {
|
||||
private readonly notifications;
|
||||
constructor(notifications: NotificationsService);
|
||||
markAllRead(): Promise<{
|
||||
ok: boolean;
|
||||
}>;
|
||||
markRead(id: string): Promise<{
|
||||
ok: boolean;
|
||||
}>;
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AdminNotificationsController = exports.CustomerNotificationsController = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const notifications_service_1 = require("./notifications.service");
|
||||
const auth_decorators_1 = require("../common/decorators/auth.decorators");
|
||||
const current_user_decorator_1 = require("../common/decorators/current-user.decorator");
|
||||
let CustomerNotificationsController = class CustomerNotificationsController {
|
||||
notifications;
|
||||
constructor(notifications) {
|
||||
this.notifications = notifications;
|
||||
}
|
||||
list(user) {
|
||||
return this.notifications.listForCustomer(user.id);
|
||||
}
|
||||
unreadCount(user) {
|
||||
return this.notifications.unreadCount(user.id);
|
||||
}
|
||||
markAllRead(user) {
|
||||
return this.notifications.markAllRead(user.id);
|
||||
}
|
||||
markRead(user, id) {
|
||||
return this.notifications.markRead(user.id, id);
|
||||
}
|
||||
};
|
||||
exports.CustomerNotificationsController = CustomerNotificationsController;
|
||||
__decorate([
|
||||
(0, common_1.Get)(),
|
||||
__param(0, (0, current_user_decorator_1.CurrentUser)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object]),
|
||||
__metadata("design:returntype", void 0)
|
||||
], CustomerNotificationsController.prototype, "list", null);
|
||||
__decorate([
|
||||
(0, common_1.Get)('unread-count'),
|
||||
__param(0, (0, current_user_decorator_1.CurrentUser)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object]),
|
||||
__metadata("design:returntype", void 0)
|
||||
], CustomerNotificationsController.prototype, "unreadCount", null);
|
||||
__decorate([
|
||||
(0, common_1.Patch)('read-all'),
|
||||
__param(0, (0, current_user_decorator_1.CurrentUser)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object]),
|
||||
__metadata("design:returntype", void 0)
|
||||
], CustomerNotificationsController.prototype, "markAllRead", null);
|
||||
__decorate([
|
||||
(0, common_1.Patch)(':id/read'),
|
||||
__param(0, (0, current_user_decorator_1.CurrentUser)()),
|
||||
__param(1, (0, common_1.Param)('id')),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object, String]),
|
||||
__metadata("design:returntype", void 0)
|
||||
], CustomerNotificationsController.prototype, "markRead", null);
|
||||
exports.CustomerNotificationsController = CustomerNotificationsController = __decorate([
|
||||
(0, common_1.Controller)('notifications'),
|
||||
(0, auth_decorators_1.AuthType)('customer'),
|
||||
__metadata("design:paramtypes", [notifications_service_1.NotificationsService])
|
||||
], CustomerNotificationsController);
|
||||
let AdminNotificationsController = class AdminNotificationsController {
|
||||
notifications;
|
||||
constructor(notifications) {
|
||||
this.notifications = notifications;
|
||||
}
|
||||
markAllRead() {
|
||||
return this.notifications.markAllAdminRead();
|
||||
}
|
||||
markRead(id) {
|
||||
return this.notifications.markAdminRead(id);
|
||||
}
|
||||
};
|
||||
exports.AdminNotificationsController = AdminNotificationsController;
|
||||
__decorate([
|
||||
(0, common_1.Patch)('read-all'),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", []),
|
||||
__metadata("design:returntype", void 0)
|
||||
], AdminNotificationsController.prototype, "markAllRead", null);
|
||||
__decorate([
|
||||
(0, common_1.Patch)(':id/read'),
|
||||
__param(0, (0, common_1.Param)('id')),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [String]),
|
||||
__metadata("design:returntype", void 0)
|
||||
], AdminNotificationsController.prototype, "markRead", null);
|
||||
exports.AdminNotificationsController = AdminNotificationsController = __decorate([
|
||||
(0, common_1.Controller)('admin/notifications'),
|
||||
(0, auth_decorators_1.AuthType)('admin'),
|
||||
__metadata("design:paramtypes", [notifications_service_1.NotificationsService])
|
||||
], AdminNotificationsController);
|
||||
//# sourceMappingURL=notifications.controller.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"notifications.controller.js","sourceRoot":"","sources":["../../src/notifications/notifications.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAA+D;AAC/D,mEAA+D;AAC/D,0EAAgE;AAChE,wFAA0E;AAKnE,IAAM,+BAA+B,GAArC,MAAM,+BAA+B;IACb;IAA7B,YAA6B,aAAmC;QAAnC,kBAAa,GAAb,aAAa,CAAsB;IAAG,CAAC;IAGpE,IAAI,CAAgB,IAAmB;QACrC,OAAO,IAAI,CAAC,aAAa,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrD,CAAC;IAGD,WAAW,CAAgB,IAAmB;QAC5C,OAAO,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjD,CAAC;IAGD,WAAW,CAAgB,IAAmB;QAC5C,OAAO,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjD,CAAC;IAGD,QAAQ,CAAgB,IAAmB,EAAe,EAAU;QAClE,OAAO,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAClD,CAAC;CACF,CAAA;AAtBY,0EAA+B;AAI1C;IADC,IAAA,YAAG,GAAE;IACA,WAAA,IAAA,oCAAW,GAAE,CAAA;;;;2DAElB;AAGD;IADC,IAAA,YAAG,EAAC,cAAc,CAAC;IACP,WAAA,IAAA,oCAAW,GAAE,CAAA;;;;kEAEzB;AAGD;IADC,IAAA,cAAK,EAAC,UAAU,CAAC;IACL,WAAA,IAAA,oCAAW,GAAE,CAAA;;;;kEAEzB;AAGD;IADC,IAAA,cAAK,EAAC,UAAU,CAAC;IACR,WAAA,IAAA,oCAAW,GAAE,CAAA;IAAuB,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;+DAExD;0CArBU,+BAA+B;IAF3C,IAAA,mBAAU,EAAC,eAAe,CAAC;IAC3B,IAAA,0BAAQ,EAAC,UAAU,CAAC;qCAEyB,4CAAoB;GADrD,+BAA+B,CAsB3C;AAIM,IAAM,4BAA4B,GAAlC,MAAM,4BAA4B;IACV;IAA7B,YAA6B,aAAmC;QAAnC,kBAAa,GAAb,aAAa,CAAsB;IAAG,CAAC;IAGpE,WAAW;QACT,OAAO,IAAI,CAAC,aAAa,CAAC,gBAAgB,EAAE,CAAC;IAC/C,CAAC;IAGD,QAAQ,CAAc,EAAU;QAC9B,OAAO,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAC9C,CAAC;CACF,CAAA;AAZY,oEAA4B;AAIvC;IADC,IAAA,cAAK,EAAC,UAAU,CAAC;;;;+DAGjB;AAGD;IADC,IAAA,cAAK,EAAC,UAAU,CAAC;IACR,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;4DAEpB;uCAXU,4BAA4B;IAFxC,IAAA,mBAAU,EAAC,qBAAqB,CAAC;IACjC,IAAA,0BAAQ,EAAC,OAAO,CAAC;qCAE4B,4CAAoB;GADrD,4BAA4B,CAYxC"}
|
||||
@@ -0,0 +1,2 @@
|
||||
export declare class NotificationsModule {
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.NotificationsModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const notifications_service_1 = require("./notifications.service");
|
||||
const notifications_controller_1 = require("./notifications.controller");
|
||||
const sms_module_1 = require("../sms/sms.module");
|
||||
let NotificationsModule = class NotificationsModule {
|
||||
};
|
||||
exports.NotificationsModule = NotificationsModule;
|
||||
exports.NotificationsModule = NotificationsModule = __decorate([
|
||||
(0, common_1.Module)({
|
||||
imports: [sms_module_1.SmsModule],
|
||||
controllers: [notifications_controller_1.CustomerNotificationsController, notifications_controller_1.AdminNotificationsController],
|
||||
providers: [notifications_service_1.NotificationsService],
|
||||
exports: [notifications_service_1.NotificationsService],
|
||||
})
|
||||
], NotificationsModule);
|
||||
//# sourceMappingURL=notifications.module.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"notifications.module.js","sourceRoot":"","sources":["../../src/notifications/notifications.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,mEAA+D;AAC/D,yEAGoC;AACpC,kDAA8C;AAQvC,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;CAAG,CAAA;AAAtB,kDAAmB;8BAAnB,mBAAmB;IAN/B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,sBAAS,CAAC;QACpB,WAAW,EAAE,CAAC,0DAA+B,EAAE,uDAA4B,CAAC;QAC5E,SAAS,EAAE,CAAC,4CAAoB,CAAC;QACjC,OAAO,EAAE,CAAC,4CAAoB,CAAC;KAChC,CAAC;GACW,mBAAmB,CAAG"}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { NotificationTone } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { SmsService } from '../sms/sms.service';
|
||||
import { type CustomerEvent } from './notification-events';
|
||||
type NotifyOptions = {
|
||||
amount?: number | null;
|
||||
note?: string | null;
|
||||
carrier?: string | null;
|
||||
trackingNumber?: string | null;
|
||||
};
|
||||
export declare class NotificationsService {
|
||||
private readonly prisma;
|
||||
private readonly sms;
|
||||
private readonly config;
|
||||
private readonly logger;
|
||||
constructor(prisma: PrismaService, sms: SmsService, config: ConfigService);
|
||||
notifyOrderEvent(orderId: string, event: CustomerEvent, options?: NotifyOptions): Promise<void>;
|
||||
dispatchOrderEvent(orderId: string, event: CustomerEvent, options?: NotifyOptions): void;
|
||||
notifyAdmin(input: {
|
||||
title: string;
|
||||
body: string;
|
||||
tone?: NotificationTone;
|
||||
link?: string;
|
||||
}): Promise<void>;
|
||||
dispatchAdmin(input: {
|
||||
title: string;
|
||||
body: string;
|
||||
tone?: NotificationTone;
|
||||
link?: string;
|
||||
}): void;
|
||||
listForCustomer(customerId: string, limit?: number): Promise<{
|
||||
id: string;
|
||||
orderId: string | undefined;
|
||||
event: string;
|
||||
title: string;
|
||||
body: string;
|
||||
tone: import("@prisma/client").$Enums.NotificationTone;
|
||||
link: string | undefined;
|
||||
isRead: boolean;
|
||||
sentAt: string;
|
||||
}[]>;
|
||||
unreadCount(customerId: string): Promise<{
|
||||
count: number;
|
||||
}>;
|
||||
markRead(customerId: string, id: string): Promise<{
|
||||
count: number;
|
||||
}>;
|
||||
markAllRead(customerId: string): Promise<{
|
||||
count: number;
|
||||
}>;
|
||||
markAdminRead(id: string): Promise<{
|
||||
ok: boolean;
|
||||
}>;
|
||||
markAllAdminRead(): Promise<{
|
||||
ok: boolean;
|
||||
}>;
|
||||
private appName;
|
||||
private isSmsEventEnabled;
|
||||
}
|
||||
export {};
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var NotificationsService_1;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.NotificationsService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const config_1 = require("@nestjs/config");
|
||||
const client_1 = require("@prisma/client");
|
||||
const prisma_service_1 = require("../prisma/prisma.service");
|
||||
const sms_service_1 = require("../sms/sms.service");
|
||||
const notification_events_1 = require("./notification-events");
|
||||
let NotificationsService = NotificationsService_1 = class NotificationsService {
|
||||
prisma;
|
||||
sms;
|
||||
config;
|
||||
logger = new common_1.Logger(NotificationsService_1.name);
|
||||
constructor(prisma, sms, config) {
|
||||
this.prisma = prisma;
|
||||
this.sms = sms;
|
||||
this.config = config;
|
||||
}
|
||||
async notifyOrderEvent(orderId, event, options = {}) {
|
||||
try {
|
||||
const order = await this.prisma.order.findUnique({
|
||||
where: { id: orderId },
|
||||
select: {
|
||||
id: true,
|
||||
orderNumber: true,
|
||||
userId: true,
|
||||
finalAmount: true,
|
||||
totalAmount: true,
|
||||
user: { select: { phone: true } },
|
||||
},
|
||||
});
|
||||
if (!order)
|
||||
return;
|
||||
const template = notification_events_1.CUSTOMER_EVENTS[event];
|
||||
const ctx = {
|
||||
orderNumber: order.orderNumber,
|
||||
appName: this.appName(),
|
||||
amount: options.amount ?? order.finalAmount ?? order.totalAmount ?? undefined,
|
||||
note: options.note ?? null,
|
||||
carrier: options.carrier ?? null,
|
||||
trackingNumber: options.trackingNumber ?? null,
|
||||
};
|
||||
const notification = await this.prisma.customerNotification.create({
|
||||
data: {
|
||||
customerId: order.userId,
|
||||
orderId: order.id,
|
||||
event,
|
||||
title: template.title,
|
||||
body: template.body(ctx),
|
||||
tone: template.tone,
|
||||
link: `/orders/${order.id}`,
|
||||
},
|
||||
});
|
||||
if (!template.sms || !template.smsSwitch)
|
||||
return;
|
||||
const enabled = await this.isSmsEventEnabled(template.smsSwitch);
|
||||
if (!enabled || !this.sms.isEnabled())
|
||||
return;
|
||||
const result = await this.sms.send(order.user.phone, template.sms(ctx));
|
||||
if (result.success) {
|
||||
await this.prisma.customerNotification.update({
|
||||
where: { id: notification.id },
|
||||
data: { smsSent: true },
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
this.logger.error(`notifyOrderEvent(${event}) failed for order ${orderId}`, err instanceof Error ? err.stack : err);
|
||||
}
|
||||
}
|
||||
dispatchOrderEvent(orderId, event, options = {}) {
|
||||
void this.notifyOrderEvent(orderId, event, options);
|
||||
}
|
||||
async notifyAdmin(input) {
|
||||
try {
|
||||
await this.prisma.adminNotification.create({
|
||||
data: {
|
||||
title: input.title,
|
||||
body: input.body,
|
||||
tone: input.tone ?? client_1.NotificationTone.info,
|
||||
link: input.link ?? null,
|
||||
},
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
this.logger.error('notifyAdmin failed', err instanceof Error ? err.stack : err);
|
||||
}
|
||||
}
|
||||
dispatchAdmin(input) {
|
||||
void this.notifyAdmin(input);
|
||||
}
|
||||
async listForCustomer(customerId, limit = 50) {
|
||||
const rows = await this.prisma.customerNotification.findMany({
|
||||
where: { customerId },
|
||||
orderBy: { sentAt: 'desc' },
|
||||
take: limit,
|
||||
});
|
||||
return rows.map((n) => ({
|
||||
id: n.id,
|
||||
orderId: n.orderId ?? undefined,
|
||||
event: n.event,
|
||||
title: n.title,
|
||||
body: n.body,
|
||||
tone: n.tone ?? client_1.NotificationTone.info,
|
||||
link: n.link ?? undefined,
|
||||
isRead: n.isRead,
|
||||
sentAt: n.sentAt.toISOString(),
|
||||
}));
|
||||
}
|
||||
async unreadCount(customerId) {
|
||||
const count = await this.prisma.customerNotification.count({
|
||||
where: { customerId, isRead: false },
|
||||
});
|
||||
return { count };
|
||||
}
|
||||
async markRead(customerId, id) {
|
||||
const existing = await this.prisma.customerNotification.findFirst({
|
||||
where: { id, customerId },
|
||||
select: { id: true },
|
||||
});
|
||||
if (!existing)
|
||||
throw new common_1.NotFoundException('اعلان یافت نشد');
|
||||
await this.prisma.customerNotification.update({
|
||||
where: { id },
|
||||
data: { isRead: true },
|
||||
});
|
||||
return this.unreadCount(customerId);
|
||||
}
|
||||
async markAllRead(customerId) {
|
||||
await this.prisma.customerNotification.updateMany({
|
||||
where: { customerId, isRead: false },
|
||||
data: { isRead: true },
|
||||
});
|
||||
return { count: 0 };
|
||||
}
|
||||
async markAdminRead(id) {
|
||||
await this.prisma.adminNotification.updateMany({
|
||||
where: { id },
|
||||
data: { isRead: true },
|
||||
});
|
||||
return { ok: true };
|
||||
}
|
||||
async markAllAdminRead() {
|
||||
await this.prisma.adminNotification.updateMany({
|
||||
where: { isRead: false },
|
||||
data: { isRead: true },
|
||||
});
|
||||
return { ok: true };
|
||||
}
|
||||
appName() {
|
||||
return this.config.get('SMS_APP_NAME', 'پارچه') ?? 'پارچه';
|
||||
}
|
||||
async isSmsEventEnabled(key) {
|
||||
const settings = await this.prisma.systemSettings.findUnique({
|
||||
where: { id: 'default' },
|
||||
select: {
|
||||
smsOrderRegistered: true,
|
||||
smsPricingReady: true,
|
||||
smsPaymentReminder: true,
|
||||
smsCheckReview: true,
|
||||
smsShippingUpdate: true,
|
||||
},
|
||||
});
|
||||
return settings ? settings[key] : true;
|
||||
}
|
||||
};
|
||||
exports.NotificationsService = NotificationsService;
|
||||
exports.NotificationsService = NotificationsService = NotificationsService_1 = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [prisma_service_1.PrismaService,
|
||||
sms_service_1.SmsService,
|
||||
config_1.ConfigService])
|
||||
], NotificationsService);
|
||||
//# sourceMappingURL=notifications.service.js.map
|
||||
File diff suppressed because one or more lines are too long
+11
@@ -0,0 +1,11 @@
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
export declare class CashInvoiceSchedulerService {
|
||||
private readonly prisma;
|
||||
private readonly notifications;
|
||||
private readonly logger;
|
||||
constructor(prisma: PrismaService, notifications: NotificationsService);
|
||||
handleCashInvoices(): Promise<void>;
|
||||
sendReminders(): Promise<number>;
|
||||
expireOverdueInvoices(): Promise<number>;
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var CashInvoiceSchedulerService_1;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CashInvoiceSchedulerService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const schedule_1 = require("@nestjs/schedule");
|
||||
const client_1 = require("@prisma/client");
|
||||
const prisma_service_1 = require("../prisma/prisma.service");
|
||||
const notifications_service_1 = require("../notifications/notifications.service");
|
||||
const PAYABLE_STATUSES = [
|
||||
client_1.OrderStatus.awaiting_cash_payment,
|
||||
client_1.OrderStatus.cash_invoice_issued,
|
||||
];
|
||||
const REMINDER_WINDOW_MS = 60 * 60 * 1000;
|
||||
let CashInvoiceSchedulerService = CashInvoiceSchedulerService_1 = class CashInvoiceSchedulerService {
|
||||
prisma;
|
||||
notifications;
|
||||
logger = new common_1.Logger(CashInvoiceSchedulerService_1.name);
|
||||
constructor(prisma, notifications) {
|
||||
this.prisma = prisma;
|
||||
this.notifications = notifications;
|
||||
}
|
||||
async handleCashInvoices() {
|
||||
await this.sendReminders();
|
||||
await this.expireOverdueInvoices();
|
||||
}
|
||||
async sendReminders() {
|
||||
const now = new Date();
|
||||
const soon = new Date(now.getTime() + REMINDER_WINDOW_MS);
|
||||
try {
|
||||
const orders = await this.prisma.order.findMany({
|
||||
where: {
|
||||
status: { in: PAYABLE_STATUSES },
|
||||
cashReminderSentAt: null,
|
||||
cashInvoiceExpiresAt: { gt: now, lte: soon },
|
||||
},
|
||||
select: { id: true, finalAmount: true },
|
||||
});
|
||||
for (const order of orders) {
|
||||
await this.prisma.order.update({
|
||||
where: { id: order.id },
|
||||
data: { cashReminderSentAt: now },
|
||||
});
|
||||
await this.notifications.notifyOrderEvent(order.id, 'cash_invoice_reminder', { amount: order.finalAmount });
|
||||
}
|
||||
return orders.length;
|
||||
}
|
||||
catch (err) {
|
||||
this.logger.error('sendReminders failed', err instanceof Error ? err.stack : err);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
async expireOverdueInvoices() {
|
||||
const now = new Date();
|
||||
try {
|
||||
const orders = await this.prisma.order.findMany({
|
||||
where: {
|
||||
status: { in: PAYABLE_STATUSES },
|
||||
cashInvoiceExpiresAt: { lt: now },
|
||||
},
|
||||
select: { id: true, orderNumber: true, status: true },
|
||||
});
|
||||
for (const order of orders) {
|
||||
await this.prisma.$transaction([
|
||||
this.prisma.order.update({
|
||||
where: { id: order.id },
|
||||
data: { status: client_1.OrderStatus.expired },
|
||||
}),
|
||||
this.prisma.orderStatusHistory.create({
|
||||
data: {
|
||||
orderId: order.id,
|
||||
fromStatus: order.status,
|
||||
toStatus: client_1.OrderStatus.expired,
|
||||
changedBy: 'system',
|
||||
note: 'پایان مهلت پرداخت فاکتور نقدی',
|
||||
},
|
||||
}),
|
||||
]);
|
||||
await this.notifications.notifyOrderEvent(order.id, 'cash_invoice_expired');
|
||||
this.notifications.dispatchAdmin({
|
||||
title: 'فاکتور نقدی منقضی شد',
|
||||
body: `مهلت پرداخت سفارش ${order.orderNumber} به پایان رسید و سفارش منقضی شد.`,
|
||||
link: '/orders',
|
||||
});
|
||||
}
|
||||
if (orders.length) {
|
||||
this.logger.log(`${orders.length} فاکتور نقدی منقضی شد`);
|
||||
}
|
||||
return orders.length;
|
||||
}
|
||||
catch (err) {
|
||||
this.logger.error('expireOverdueInvoices failed', err instanceof Error ? err.stack : err);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.CashInvoiceSchedulerService = CashInvoiceSchedulerService;
|
||||
__decorate([
|
||||
(0, schedule_1.Cron)(schedule_1.CronExpression.EVERY_10_MINUTES),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", []),
|
||||
__metadata("design:returntype", Promise)
|
||||
], CashInvoiceSchedulerService.prototype, "handleCashInvoices", null);
|
||||
exports.CashInvoiceSchedulerService = CashInvoiceSchedulerService = CashInvoiceSchedulerService_1 = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [prisma_service_1.PrismaService,
|
||||
notifications_service_1.NotificationsService])
|
||||
], CashInvoiceSchedulerService);
|
||||
//# sourceMappingURL=cash-invoice-scheduler.service.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"cash-invoice-scheduler.service.js","sourceRoot":"","sources":["../../src/orders/cash-invoice-scheduler.service.ts"],"names":[],"mappings":"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"}
|
||||
Vendored
+6
-1
@@ -22,13 +22,17 @@ export declare class SubmitOrderCheckDto {
|
||||
}
|
||||
export declare class SubmitOrderDto {
|
||||
companyId: string;
|
||||
settlementType: SettlementType;
|
||||
settlementType?: SettlementType;
|
||||
settlementMethod?: SettlementType;
|
||||
checkMode?: CheckRegistrationMode;
|
||||
notes?: string;
|
||||
items: SubmitOrderItemDto[];
|
||||
checks?: SubmitOrderCheckDto[];
|
||||
}
|
||||
export declare class ChooseSettlementDto {
|
||||
settlementType: SettlementType;
|
||||
checkMode?: CheckRegistrationMode;
|
||||
}
|
||||
export declare class AdminCreateOrderDto {
|
||||
userId?: string;
|
||||
customerPhone?: string;
|
||||
@@ -57,6 +61,7 @@ export declare class UpdateOrderStatusDto {
|
||||
status: OrderStatus;
|
||||
note?: string;
|
||||
reason?: string;
|
||||
force?: boolean;
|
||||
}
|
||||
export declare class UpdateOrderChecksDto {
|
||||
checks: SubmitOrderCheckDto[];
|
||||
|
||||
Vendored
+23
-1
@@ -9,7 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.UpdateOrderChecksDto = exports.UpdateOrderStatusDto = exports.UpdateOrderPricingDto = exports.OrderItemPricingDto = exports.AdminCreateOrderDto = exports.SubmitOrderDto = exports.SubmitOrderCheckDto = exports.SubmitOrderItemDto = void 0;
|
||||
exports.UpdateOrderChecksDto = exports.UpdateOrderStatusDto = exports.UpdateOrderPricingDto = exports.OrderItemPricingDto = exports.AdminCreateOrderDto = exports.ChooseSettlementDto = exports.SubmitOrderDto = exports.SubmitOrderCheckDto = exports.SubmitOrderItemDto = void 0;
|
||||
const class_validator_1 = require("class-validator");
|
||||
const class_transformer_1 = require("class-transformer");
|
||||
const client_1 = require("@prisma/client");
|
||||
@@ -137,6 +137,7 @@ __decorate([
|
||||
__metadata("design:type", String)
|
||||
], SubmitOrderDto.prototype, "companyId", void 0);
|
||||
__decorate([
|
||||
(0, class_validator_1.IsOptional)(),
|
||||
(0, class_validator_1.IsEnum)(client_1.SettlementType),
|
||||
__metadata("design:type", String)
|
||||
], SubmitOrderDto.prototype, "settlementType", void 0);
|
||||
@@ -169,6 +170,20 @@ __decorate([
|
||||
(0, class_transformer_1.Type)(() => SubmitOrderCheckDto),
|
||||
__metadata("design:type", Array)
|
||||
], SubmitOrderDto.prototype, "checks", void 0);
|
||||
class ChooseSettlementDto {
|
||||
settlementType;
|
||||
checkMode;
|
||||
}
|
||||
exports.ChooseSettlementDto = ChooseSettlementDto;
|
||||
__decorate([
|
||||
(0, class_validator_1.IsEnum)(client_1.SettlementType),
|
||||
__metadata("design:type", String)
|
||||
], ChooseSettlementDto.prototype, "settlementType", void 0);
|
||||
__decorate([
|
||||
(0, class_validator_1.IsOptional)(),
|
||||
(0, class_validator_1.IsEnum)(client_1.CheckRegistrationMode),
|
||||
__metadata("design:type", String)
|
||||
], ChooseSettlementDto.prototype, "checkMode", void 0);
|
||||
class AdminCreateOrderDto {
|
||||
userId;
|
||||
customerPhone;
|
||||
@@ -305,6 +320,7 @@ class UpdateOrderStatusDto {
|
||||
status;
|
||||
note;
|
||||
reason;
|
||||
force;
|
||||
}
|
||||
exports.UpdateOrderStatusDto = UpdateOrderStatusDto;
|
||||
__decorate([
|
||||
@@ -321,6 +337,12 @@ __decorate([
|
||||
(0, class_validator_1.IsString)(),
|
||||
__metadata("design:type", String)
|
||||
], UpdateOrderStatusDto.prototype, "reason", void 0);
|
||||
__decorate([
|
||||
(0, class_validator_1.IsOptional)(),
|
||||
(0, class_transformer_1.Type)(() => Boolean),
|
||||
(0, class_validator_1.IsBoolean)(),
|
||||
__metadata("design:type", Boolean)
|
||||
], UpdateOrderStatusDto.prototype, "force", void 0);
|
||||
class UpdateOrderChecksDto {
|
||||
checks;
|
||||
checkMode;
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+158
-37
@@ -1,6 +1,6 @@
|
||||
import { OrdersService } from './orders.service';
|
||||
import type { AuthPrincipal } from '../common/decorators/current-user.decorator';
|
||||
import { AdminCreateOrderDto, SubmitOrderDto, UpdateOrderChecksDto, UpdateOrderPricingDto, UpdateOrderStatusDto } from './dto/order.dto';
|
||||
import { AdminCreateOrderDto, ChooseSettlementDto, SubmitOrderDto, UpdateOrderChecksDto, UpdateOrderPricingDto, UpdateOrderStatusDto } from './dto/order.dto';
|
||||
export declare class CustomerOrdersController {
|
||||
private readonly ordersService;
|
||||
constructor(ordersService: OrdersService);
|
||||
@@ -10,17 +10,19 @@ export declare class CustomerOrdersController {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -42,8 +44,8 @@ export declare class CustomerOrdersController {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -111,17 +113,19 @@ export declare class CustomerOrdersController {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -143,8 +147,8 @@ export declare class CustomerOrdersController {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -212,17 +216,19 @@ export declare class CustomerOrdersController {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -244,8 +250,111 @@ export declare class CustomerOrdersController {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
}[];
|
||||
checks: {
|
||||
id: string;
|
||||
orderId: string;
|
||||
row: number;
|
||||
amount: number;
|
||||
dueDate: string;
|
||||
sayadId: string | undefined;
|
||||
checkNumber: string | undefined;
|
||||
ownerNationalId: string | undefined;
|
||||
ownerFirstName: string | undefined;
|
||||
ownerLastName: string | undefined;
|
||||
bankName: string;
|
||||
branchName: string | undefined;
|
||||
frontImage: string | undefined;
|
||||
backImage: string | undefined;
|
||||
notes: string | undefined;
|
||||
mode: import("@prisma/client").$Enums.CheckRegistrationMode;
|
||||
status: import("@prisma/client").$Enums.CheckStatus;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}[];
|
||||
cashReceipt: {
|
||||
id: string;
|
||||
amount: number;
|
||||
paidAmount: number | null;
|
||||
transferDate: string | null;
|
||||
transferTime: string | undefined;
|
||||
trackingNumber: string | undefined;
|
||||
trackingCode: string | undefined;
|
||||
destinationAccountId: string | undefined;
|
||||
receiptImage: string | undefined;
|
||||
notes: string | undefined;
|
||||
expiresAt: string | undefined;
|
||||
status: import("@prisma/client").$Enums.CashPaymentStatus;
|
||||
submittedAt: string | undefined;
|
||||
createdAt: string;
|
||||
} | undefined;
|
||||
shipment: {
|
||||
id: string;
|
||||
orderId: string;
|
||||
carrier: string;
|
||||
trackingNumber: string;
|
||||
meterage: number;
|
||||
status: import("@prisma/client").$Enums.ShipmentStatus;
|
||||
shippedAt: string | null;
|
||||
deliveredAt: string | null;
|
||||
} | undefined;
|
||||
statusHistory: {
|
||||
id: string;
|
||||
fromStatus: import("@prisma/client").$Enums.OrderStatus | null;
|
||||
toStatus: import("@prisma/client").$Enums.OrderStatus;
|
||||
changedBy: string;
|
||||
note: string | undefined;
|
||||
reason: string | undefined;
|
||||
changedAt: string;
|
||||
}[];
|
||||
}>;
|
||||
chooseSettlement(user: AuthPrincipal, id: string, dto: ChooseSettlementDto): Promise<{
|
||||
id: string;
|
||||
orderNumber: string;
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
cashInvoiceExpiresAt: string | undefined;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
customerName: string;
|
||||
customerPhone: string;
|
||||
customerNationalId: string | undefined;
|
||||
customerType: import("@prisma/client").$Enums.CustomerType;
|
||||
items: {
|
||||
id: string;
|
||||
productId: string | undefined;
|
||||
catalogCode: string;
|
||||
productName: string;
|
||||
productCode: string | undefined;
|
||||
color: string;
|
||||
requestedMeterage: number;
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -313,17 +422,19 @@ export declare class CustomerOrdersController {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -345,8 +456,8 @@ export declare class CustomerOrdersController {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -418,17 +529,19 @@ export declare class AdminOrdersController {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -450,8 +563,8 @@ export declare class AdminOrdersController {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -519,17 +632,19 @@ export declare class AdminOrdersController {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -551,8 +666,8 @@ export declare class AdminOrdersController {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -620,17 +735,19 @@ export declare class AdminOrdersController {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -652,8 +769,8 @@ export declare class AdminOrdersController {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -721,17 +838,19 @@ export declare class AdminOrdersController {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -753,8 +872,8 @@ export declare class AdminOrdersController {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -822,17 +941,19 @@ export declare class AdminOrdersController {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -854,8 +975,8 @@ export declare class AdminOrdersController {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
|
||||
Vendored
+13
@@ -32,6 +32,9 @@ let CustomerOrdersController = class CustomerOrdersController {
|
||||
submit(user, dto) {
|
||||
return this.ordersService.submitOrder(user.id, dto);
|
||||
}
|
||||
chooseSettlement(user, id, dto) {
|
||||
return this.ordersService.chooseSettlement(user.id, id, dto);
|
||||
}
|
||||
updateChecks(user, id, dto) {
|
||||
return this.ordersService.updateChecks(user.id, id, dto);
|
||||
}
|
||||
@@ -63,6 +66,16 @@ __decorate([
|
||||
__metadata("design:paramtypes", [Object, order_dto_1.SubmitOrderDto]),
|
||||
__metadata("design:returntype", void 0)
|
||||
], CustomerOrdersController.prototype, "submit", null);
|
||||
__decorate([
|
||||
(0, auth_decorators_1.AuthType)('customer'),
|
||||
(0, common_1.Post)(':id/settlement'),
|
||||
__param(0, (0, current_user_decorator_1.CurrentUser)()),
|
||||
__param(1, (0, common_1.Param)('id')),
|
||||
__param(2, (0, common_1.Body)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object, String, order_dto_1.ChooseSettlementDto]),
|
||||
__metadata("design:returntype", void 0)
|
||||
], CustomerOrdersController.prototype, "chooseSettlement", null);
|
||||
__decorate([
|
||||
(0, auth_decorators_1.AuthType)('customer'),
|
||||
(0, common_1.Patch)(':id/checks'),
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"orders.controller.js","sourceRoot":"","sources":["../../src/orders/orders.controller.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"orders.controller.js","sourceRoot":"","sources":["../../src/orders/orders.controller.ts"],"names":[],"mappings":"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"}
|
||||
Vendored
+4
-1
@@ -10,13 +10,16 @@ exports.OrdersModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const orders_controller_1 = require("./orders.controller");
|
||||
const orders_service_1 = require("./orders.service");
|
||||
const cash_invoice_scheduler_service_1 = require("./cash-invoice-scheduler.service");
|
||||
const notifications_module_1 = require("../notifications/notifications.module");
|
||||
let OrdersModule = class OrdersModule {
|
||||
};
|
||||
exports.OrdersModule = OrdersModule;
|
||||
exports.OrdersModule = OrdersModule = __decorate([
|
||||
(0, common_1.Module)({
|
||||
imports: [notifications_module_1.NotificationsModule],
|
||||
controllers: [orders_controller_1.CustomerOrdersController, orders_controller_1.AdminOrdersController],
|
||||
providers: [orders_service_1.OrdersService],
|
||||
providers: [orders_service_1.OrdersService, cash_invoice_scheduler_service_1.CashInvoiceSchedulerService],
|
||||
exports: [orders_service_1.OrdersService],
|
||||
})
|
||||
], OrdersModule);
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"orders.module.js","sourceRoot":"","sources":["../../src/orders/orders.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2DAG6B;AAC7B,qDAAiD;AAO1C,IAAM,YAAY,GAAlB,MAAM,YAAY;CAAG,CAAA;AAAf,oCAAY;uBAAZ,YAAY;IALxB,IAAA,eAAM,EAAC;QACN,WAAW,EAAE,CAAC,4CAAwB,EAAE,yCAAqB,CAAC;QAC9D,SAAS,EAAE,CAAC,8BAAa,CAAC;QAC1B,OAAO,EAAE,CAAC,8BAAa,CAAC;KACzB,CAAC;GACW,YAAY,CAAG"}
|
||||
{"version":3,"file":"orders.module.js","sourceRoot":"","sources":["../../src/orders/orders.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2DAG6B;AAC7B,qDAAiD;AACjD,qFAA+E;AAC/E,gFAA4E;AAQrE,IAAM,YAAY,GAAlB,MAAM,YAAY;CAAG,CAAA;AAAf,oCAAY;uBAAZ,YAAY;IANxB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,0CAAmB,CAAC;QAC9B,WAAW,EAAE,CAAC,4CAAwB,EAAE,yCAAqB,CAAC;QAC9D,SAAS,EAAE,CAAC,8BAAa,EAAE,4DAA2B,CAAC;QACvD,OAAO,EAAE,CAAC,8BAAa,CAAC;KACzB,CAAC;GACW,YAAY,CAAG"}
|
||||
Vendored
+167
-42
@@ -1,6 +1,7 @@
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { AdminCreateOrderDto, SubmitOrderDto, UpdateOrderChecksDto, UpdateOrderPricingDto, UpdateOrderStatusDto } from './dto/order.dto';
|
||||
import { AdminCreateOrderDto, ChooseSettlementDto, SubmitOrderDto, UpdateOrderChecksDto, UpdateOrderPricingDto, UpdateOrderStatusDto } from './dto/order.dto';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
declare const orderInclude: {
|
||||
items: true;
|
||||
checks: {
|
||||
@@ -23,24 +24,27 @@ type OrderFull = Prisma.OrderGetPayload<{
|
||||
}>;
|
||||
export declare class OrdersService {
|
||||
private readonly prisma;
|
||||
constructor(prisma: PrismaService);
|
||||
private readonly notifications;
|
||||
constructor(prisma: PrismaService, notifications: NotificationsService);
|
||||
findMyOrders(userId: string): Promise<{
|
||||
id: string;
|
||||
orderNumber: string;
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -62,8 +66,8 @@ export declare class OrdersService {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -131,17 +135,19 @@ export declare class OrdersService {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -163,8 +169,8 @@ export declare class OrdersService {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -235,17 +241,19 @@ export declare class OrdersService {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -267,8 +275,8 @@ export declare class OrdersService {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -336,17 +344,19 @@ export declare class OrdersService {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -368,8 +378,8 @@ export declare class OrdersService {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -437,17 +447,19 @@ export declare class OrdersService {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -469,8 +481,111 @@ export declare class OrdersService {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
}[];
|
||||
checks: {
|
||||
id: string;
|
||||
orderId: string;
|
||||
row: number;
|
||||
amount: number;
|
||||
dueDate: string;
|
||||
sayadId: string | undefined;
|
||||
checkNumber: string | undefined;
|
||||
ownerNationalId: string | undefined;
|
||||
ownerFirstName: string | undefined;
|
||||
ownerLastName: string | undefined;
|
||||
bankName: string;
|
||||
branchName: string | undefined;
|
||||
frontImage: string | undefined;
|
||||
backImage: string | undefined;
|
||||
notes: string | undefined;
|
||||
mode: import("@prisma/client").$Enums.CheckRegistrationMode;
|
||||
status: import("@prisma/client").$Enums.CheckStatus;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}[];
|
||||
cashReceipt: {
|
||||
id: string;
|
||||
amount: number;
|
||||
paidAmount: number | null;
|
||||
transferDate: string | null;
|
||||
transferTime: string | undefined;
|
||||
trackingNumber: string | undefined;
|
||||
trackingCode: string | undefined;
|
||||
destinationAccountId: string | undefined;
|
||||
receiptImage: string | undefined;
|
||||
notes: string | undefined;
|
||||
expiresAt: string | undefined;
|
||||
status: import("@prisma/client").$Enums.CashPaymentStatus;
|
||||
submittedAt: string | undefined;
|
||||
createdAt: string;
|
||||
} | undefined;
|
||||
shipment: {
|
||||
id: string;
|
||||
orderId: string;
|
||||
carrier: string;
|
||||
trackingNumber: string;
|
||||
meterage: number;
|
||||
status: import("@prisma/client").$Enums.ShipmentStatus;
|
||||
shippedAt: string | null;
|
||||
deliveredAt: string | null;
|
||||
} | undefined;
|
||||
statusHistory: {
|
||||
id: string;
|
||||
fromStatus: import("@prisma/client").$Enums.OrderStatus | null;
|
||||
toStatus: import("@prisma/client").$Enums.OrderStatus;
|
||||
changedBy: string;
|
||||
note: string | undefined;
|
||||
reason: string | undefined;
|
||||
changedAt: string;
|
||||
}[];
|
||||
}>;
|
||||
chooseSettlement(userId: string, id: string, dto: ChooseSettlementDto): Promise<{
|
||||
id: string;
|
||||
orderNumber: string;
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
cashInvoiceExpiresAt: string | undefined;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
customerName: string;
|
||||
customerPhone: string;
|
||||
customerNationalId: string | undefined;
|
||||
customerType: import("@prisma/client").$Enums.CustomerType;
|
||||
items: {
|
||||
id: string;
|
||||
productId: string | undefined;
|
||||
catalogCode: string;
|
||||
productName: string;
|
||||
productCode: string | undefined;
|
||||
color: string;
|
||||
requestedMeterage: number;
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -538,17 +653,19 @@ export declare class OrdersService {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -570,8 +687,8 @@ export declare class OrdersService {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -639,17 +756,19 @@ export declare class OrdersService {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -671,8 +790,8 @@ export declare class OrdersService {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -740,17 +859,19 @@ export declare class OrdersService {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -772,8 +893,8 @@ export declare class OrdersService {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -841,17 +962,19 @@ export declare class OrdersService {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -873,8 +996,8 @@ export declare class OrdersService {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
@@ -946,17 +1069,19 @@ export declare class OrdersService {
|
||||
userId: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType;
|
||||
settlementType: import("@prisma/client").$Enums.SettlementType | null;
|
||||
settlementMethod: import("@prisma/client").$Enums.SettlementType | null;
|
||||
checkMode: import("@prisma/client").$Enums.CheckRegistrationMode | undefined;
|
||||
status: import("@prisma/client").$Enums.OrderStatus;
|
||||
adminStatus: string;
|
||||
allowedTransitions: import("@prisma/client").$Enums.OrderStatus[];
|
||||
totalMeterage: number;
|
||||
meterage: number;
|
||||
totalAmount: number | null;
|
||||
shippingCost: number | null;
|
||||
totalDiscount: number | null;
|
||||
finalAmount: number | null;
|
||||
pricesVisible: boolean;
|
||||
notes: string | undefined;
|
||||
checkRassDays: number | undefined;
|
||||
checkRassDate: string | undefined;
|
||||
@@ -978,8 +1103,8 @@ export declare class OrdersService {
|
||||
confirmedMeterage: number | null;
|
||||
unitPrice: number | null;
|
||||
supplyPrice: number | null;
|
||||
unitDiscount: number;
|
||||
discount: number;
|
||||
unitDiscount: number | null;
|
||||
discount: number | null;
|
||||
lineTotal: number | null;
|
||||
totalAmount: number | null;
|
||||
notes: string | undefined;
|
||||
|
||||
Vendored
+132
-72
@@ -17,6 +17,10 @@ const order_number_1 = require("../common/utils/order-number");
|
||||
const check_rass_1 = require("../common/utils/check-rass");
|
||||
const cash_expiry_1 = require("../common/utils/cash-expiry");
|
||||
const order_status_1 = require("../common/utils/order-status");
|
||||
const order_transitions_1 = require("../common/utils/order-transitions");
|
||||
const meterage_1 = require("../common/utils/meterage");
|
||||
const notifications_service_1 = require("../notifications/notifications.service");
|
||||
const notification_events_1 = require("../notifications/notification-events");
|
||||
const name_1 = require("../common/utils/name");
|
||||
const phone_1 = require("../common/utils/phone");
|
||||
const orderInclude = {
|
||||
@@ -32,19 +36,21 @@ function roundMoney(value) {
|
||||
return Math.round(value * 100) / 100;
|
||||
}
|
||||
const CHECK_EDITABLE_STATUSES = [
|
||||
client_1.OrderStatus.awaiting_review,
|
||||
client_1.OrderStatus.pricing,
|
||||
client_1.OrderStatus.needs_correction,
|
||||
client_1.OrderStatus.awaiting_settlement_fix,
|
||||
client_1.OrderStatus.check_in_progress,
|
||||
client_1.OrderStatus.awaiting_check_review,
|
||||
client_1.OrderStatus.check_needs_fix,
|
||||
client_1.OrderStatus.check_rejected,
|
||||
];
|
||||
const PRICE_HIDDEN_STATUSES = [
|
||||
client_1.OrderStatus.awaiting_review,
|
||||
client_1.OrderStatus.pricing,
|
||||
];
|
||||
let OrdersService = class OrdersService {
|
||||
prisma;
|
||||
constructor(prisma) {
|
||||
notifications;
|
||||
constructor(prisma, notifications) {
|
||||
this.prisma = prisma;
|
||||
this.notifications = notifications;
|
||||
}
|
||||
async findMyOrders(userId) {
|
||||
const orders = await this.prisma.order.findMany({
|
||||
@@ -95,12 +101,17 @@ let OrdersService = class OrdersService {
|
||||
return this.mapOrder(order, true);
|
||||
}
|
||||
async submitOrder(userId, dto) {
|
||||
const company = await this.prisma.company.findUnique({
|
||||
where: { id: dto.companyId },
|
||||
});
|
||||
const [company, settings] = await Promise.all([
|
||||
this.prisma.company.findUnique({ where: { id: dto.companyId } }),
|
||||
this.prisma.systemSettings.findUnique({
|
||||
where: { id: 'default' },
|
||||
select: { maxMeteragePerInvoice: true },
|
||||
}),
|
||||
]);
|
||||
if (!company || !company.isActive) {
|
||||
throw new common_1.NotFoundException('شرکت یافت نشد یا غیرفعال است');
|
||||
}
|
||||
const maxMeterage = (0, meterage_1.resolveMaxMeterage)(company.maxMeteragePerInvoice, settings?.maxMeteragePerInvoice);
|
||||
const productIds = dto.items.map((i) => i.productId);
|
||||
const products = await this.prisma.product.findMany({
|
||||
where: { id: { in: productIds }, companyId: company.id, isActive: true },
|
||||
@@ -128,31 +139,12 @@ let OrdersService = class OrdersService {
|
||||
productCode: product.code,
|
||||
color: product.color,
|
||||
requestedMeterage: item.meterage,
|
||||
unitPrice: product.basePrice,
|
||||
notes: item.notes?.trim() || null,
|
||||
};
|
||||
});
|
||||
if (totalMeterage > company.maxMeteragePerInvoice) {
|
||||
throw new common_1.BadRequestException(`حداکثر متراژ مجاز هر فاکتور ${company.maxMeteragePerInvoice} متر است`);
|
||||
if (totalMeterage > maxMeterage) {
|
||||
throw new common_1.BadRequestException(`حداکثر متراژ مجاز هر فاکتور ${maxMeterage} متر است`);
|
||||
}
|
||||
const settlementType = dto.settlementType;
|
||||
if (settlementType === client_1.SettlementType.check) {
|
||||
if (!dto.checks?.length) {
|
||||
throw new common_1.BadRequestException('برای تسویه چکی حداقل یک چک لازم است');
|
||||
}
|
||||
}
|
||||
const checkMode = settlementType === client_1.SettlementType.check
|
||||
? (dto.checkMode ?? client_1.CheckRegistrationMode.full)
|
||||
: null;
|
||||
const rass = settlementType === client_1.SettlementType.check && dto.checks?.length
|
||||
? (0, check_rass_1.calculateCheckRass)(dto.checks.map((c) => ({
|
||||
amount: c.amount,
|
||||
dueDate: c.dueDate,
|
||||
})))
|
||||
: null;
|
||||
const cashInvoiceExpiresAt = settlementType === client_1.SettlementType.cash
|
||||
? (0, cash_expiry_1.computeCashInvoiceExpiry)(company)
|
||||
: null;
|
||||
const order = await this.createWithUniqueOrderNumber((orderNumber) => this.prisma.$transaction(async (tx) => {
|
||||
const created = await tx.order.create({
|
||||
data: {
|
||||
@@ -160,33 +152,19 @@ let OrdersService = class OrdersService {
|
||||
userId,
|
||||
companyId: company.id,
|
||||
companyName: company.name,
|
||||
settlementType,
|
||||
checkMode,
|
||||
settlementType: null,
|
||||
checkMode: null,
|
||||
status: client_1.OrderStatus.awaiting_review,
|
||||
totalMeterage,
|
||||
notes: dto.notes?.trim() || null,
|
||||
checkRassDays: rass?.rassDays ?? null,
|
||||
checkRassDate: rass?.rassDate ? new Date(rass.rassDate) : null,
|
||||
cashInvoiceExpiresAt,
|
||||
items: { create: itemsData },
|
||||
checks: settlementType === client_1.SettlementType.check && dto.checks
|
||||
? {
|
||||
create: dto.checks.map((c, index) => this.mapCheckCreate(c, index + 1, checkMode)),
|
||||
}
|
||||
: undefined,
|
||||
statusHistory: {
|
||||
create: [
|
||||
{
|
||||
fromStatus: null,
|
||||
toStatus: client_1.OrderStatus.submitted,
|
||||
changedBy: 'customer',
|
||||
note: 'سفارش ثبت شد',
|
||||
},
|
||||
{
|
||||
fromStatus: client_1.OrderStatus.submitted,
|
||||
toStatus: client_1.OrderStatus.awaiting_review,
|
||||
changedBy: 'customer',
|
||||
note: 'ارسال برای بررسی مدیریت',
|
||||
note: 'سفارش ثبت شد و برای بررسی مدیریت ارسال شد',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -201,13 +179,57 @@ let OrdersService = class OrdersService {
|
||||
}
|
||||
return created;
|
||||
}));
|
||||
this.notifications.dispatchOrderEvent(order.id, 'order_submitted');
|
||||
this.notifications.dispatchAdmin({
|
||||
title: 'سفارش جدید ثبت شد',
|
||||
body: `سفارش ${order.orderNumber} با ${order.items.length} قلم و ${order.totalMeterage} متر در انتظار قیمتگذاری است.`,
|
||||
link: `/orders/${order.id}`,
|
||||
});
|
||||
return this.mapOrder(order);
|
||||
}
|
||||
async adminCreate(adminId, dto) {
|
||||
const settlementType = dto.settlementType ?? dto.settlementMethod;
|
||||
if (!settlementType) {
|
||||
throw new common_1.BadRequestException('نوع تسویه الزامی است');
|
||||
async chooseSettlement(userId, id, dto) {
|
||||
const order = await this.getOrder(id);
|
||||
if (order.userId !== userId) {
|
||||
throw new common_1.ForbiddenException('دسترسی به این سفارش مجاز نیست');
|
||||
}
|
||||
if (order.status !== client_1.OrderStatus.awaiting_settlement) {
|
||||
throw new common_1.BadRequestException('انتخاب روش تسویه فقط پس از صدور فاکتور توسط مدیریت امکانپذیر است');
|
||||
}
|
||||
const isCash = dto.settlementType === client_1.SettlementType.cash;
|
||||
const nextStatus = isCash
|
||||
? client_1.OrderStatus.awaiting_cash_payment
|
||||
: client_1.OrderStatus.check_in_progress;
|
||||
(0, order_transitions_1.assertTransition)(order.status, nextStatus);
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
await tx.order.update({
|
||||
where: { id },
|
||||
data: {
|
||||
settlementType: dto.settlementType,
|
||||
checkMode: isCash
|
||||
? null
|
||||
: (dto.checkMode ?? client_1.CheckRegistrationMode.full),
|
||||
cashInvoiceExpiresAt: isCash
|
||||
? (0, cash_expiry_1.computeCashInvoiceExpiry)(order.company)
|
||||
: null,
|
||||
cashReminderSentAt: null,
|
||||
status: nextStatus,
|
||||
},
|
||||
});
|
||||
await tx.orderStatusHistory.create({
|
||||
data: {
|
||||
orderId: id,
|
||||
fromStatus: order.status,
|
||||
toStatus: nextStatus,
|
||||
changedBy: 'customer',
|
||||
note: isCash
|
||||
? 'مشتری تسویه نقدی را انتخاب کرد'
|
||||
: 'مشتری تسویه چکی را انتخاب کرد',
|
||||
},
|
||||
});
|
||||
});
|
||||
return this.findMyOrder(userId, id);
|
||||
}
|
||||
async adminCreate(adminId, dto) {
|
||||
const company = await this.prisma.company.findUnique({
|
||||
where: { id: dto.companyId },
|
||||
});
|
||||
@@ -245,7 +267,7 @@ let OrdersService = class OrdersService {
|
||||
userId: ownerId,
|
||||
companyId: company.id,
|
||||
companyName: company.name,
|
||||
settlementType,
|
||||
settlementType: null,
|
||||
status: client_1.OrderStatus.awaiting_review,
|
||||
totalMeterage,
|
||||
notes: dto.notes?.trim() || null,
|
||||
@@ -338,6 +360,18 @@ let OrdersService = class OrdersService {
|
||||
if (order.status === dto.status) {
|
||||
return this.mapOrder(order, true);
|
||||
}
|
||||
if (dto.force) {
|
||||
if (!dto.reason?.trim()) {
|
||||
throw new common_1.BadRequestException('برای تغییر دستی وضعیت، ذکر دلیل الزامی است');
|
||||
}
|
||||
}
|
||||
else {
|
||||
(0, order_transitions_1.assertTransition)(order.status, dto.status);
|
||||
}
|
||||
if (dto.status === client_1.OrderStatus.awaiting_settlement &&
|
||||
(order.finalAmount ?? 0) <= 0) {
|
||||
throw new common_1.BadRequestException('ابتدا سفارش را قیمتگذاری کنید تا فاکتور قابل ارسال باشد');
|
||||
}
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
await tx.order.update({
|
||||
where: { id },
|
||||
@@ -349,7 +383,9 @@ let OrdersService = class OrdersService {
|
||||
fromStatus: order.status,
|
||||
toStatus: dto.status,
|
||||
changedBy: `admin:${adminId}`,
|
||||
note: dto.note || null,
|
||||
note: dto.force
|
||||
? `تغییر دستی وضعیت${dto.note ? ` — ${dto.note}` : ''}`
|
||||
: dto.note || null,
|
||||
reason: dto.reason || null,
|
||||
},
|
||||
});
|
||||
@@ -365,6 +401,12 @@ let OrdersService = class OrdersService {
|
||||
}
|
||||
}
|
||||
});
|
||||
const event = (0, notification_events_1.eventForStatus)(dto.status);
|
||||
if (event) {
|
||||
this.notifications.dispatchOrderEvent(id, event, {
|
||||
note: dto.reason ?? dto.note ?? null,
|
||||
});
|
||||
}
|
||||
return this.findAdminOrder(id);
|
||||
}
|
||||
async updateChecks(userId, id, dto) {
|
||||
@@ -378,8 +420,18 @@ let OrdersService = class OrdersService {
|
||||
if (!CHECK_EDITABLE_STATUSES.includes(order.status)) {
|
||||
throw new common_1.BadRequestException('در وضعیت فعلی سفارش امکان ویرایش چکها وجود ندارد');
|
||||
}
|
||||
if (!dto.checks.length) {
|
||||
throw new common_1.BadRequestException('حداقل یک چک لازم است');
|
||||
}
|
||||
const checkMode = dto.checkMode ?? order.checkMode ?? client_1.CheckRegistrationMode.full;
|
||||
const rass = (0, check_rass_1.calculateCheckRass)(dto.checks.map((c) => ({ amount: c.amount, dueDate: c.dueDate })), order.createdAt);
|
||||
const goesToReview = order.status === client_1.OrderStatus.check_in_progress ||
|
||||
order.status === client_1.OrderStatus.check_needs_fix ||
|
||||
order.status === client_1.OrderStatus.check_rejected;
|
||||
const nextStatus = goesToReview
|
||||
? client_1.OrderStatus.awaiting_check_review
|
||||
: order.status;
|
||||
const isFix = order.status !== client_1.OrderStatus.check_in_progress;
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
await tx.check.deleteMany({ where: { orderId: id } });
|
||||
await tx.check.createMany({
|
||||
@@ -394,23 +446,30 @@ let OrdersService = class OrdersService {
|
||||
checkMode,
|
||||
checkRassDays: rass.rassDays,
|
||||
checkRassDate: rass.rassDate ? new Date(rass.rassDate) : null,
|
||||
status: order.status === client_1.OrderStatus.check_needs_fix
|
||||
? client_1.OrderStatus.awaiting_check_review
|
||||
: order.status,
|
||||
status: nextStatus,
|
||||
},
|
||||
});
|
||||
if (order.status === client_1.OrderStatus.check_needs_fix) {
|
||||
if (nextStatus !== order.status) {
|
||||
await tx.orderStatusHistory.create({
|
||||
data: {
|
||||
orderId: id,
|
||||
fromStatus: order.status,
|
||||
toStatus: client_1.OrderStatus.awaiting_check_review,
|
||||
toStatus: nextStatus,
|
||||
changedBy: 'customer',
|
||||
note: 'چکها اصلاح و مجدداً ارسال شد',
|
||||
note: isFix
|
||||
? 'چکها اصلاح و مجدداً ارسال شد'
|
||||
: 'چکها برای بررسی ارسال شد',
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
if (nextStatus !== order.status) {
|
||||
this.notifications.dispatchAdmin({
|
||||
title: isFix ? 'چکهای اصلاحشده رسید' : 'چکهای سفارش ثبت شد',
|
||||
body: `مشتری ${dto.checks.length} چک برای سفارش ${order.orderNumber} ثبت کرد و منتظر بررسی است.`,
|
||||
link: `/orders/${id}`,
|
||||
});
|
||||
}
|
||||
return this.findMyOrder(userId, id);
|
||||
}
|
||||
mapCheckCreate(c, row, defaultMode) {
|
||||
@@ -467,6 +526,7 @@ let OrdersService = class OrdersService {
|
||||
}
|
||||
mapOrder(order, adminView = false) {
|
||||
const customerName = (0, name_1.joinFullName)(order.user.firstName, order.user.lastName);
|
||||
const hidePrices = !adminView && PRICE_HIDDEN_STATUSES.includes(order.status);
|
||||
const base = {
|
||||
id: order.id,
|
||||
orderNumber: order.orderNumber,
|
||||
@@ -478,12 +538,14 @@ let OrdersService = class OrdersService {
|
||||
checkMode: order.checkMode ?? undefined,
|
||||
status: order.status,
|
||||
adminStatus: (0, order_status_1.toAdminStatus)(order.status),
|
||||
allowedTransitions: adminView ? (0, order_transitions_1.allowedTransitions)(order.status) : [],
|
||||
totalMeterage: order.totalMeterage,
|
||||
meterage: order.totalMeterage,
|
||||
totalAmount: order.totalAmount,
|
||||
shippingCost: order.shippingCost,
|
||||
totalDiscount: order.totalDiscount,
|
||||
finalAmount: order.finalAmount,
|
||||
totalAmount: hidePrices ? null : order.totalAmount,
|
||||
shippingCost: hidePrices ? null : order.shippingCost,
|
||||
totalDiscount: hidePrices ? null : order.totalDiscount,
|
||||
finalAmount: hidePrices ? null : order.finalAmount,
|
||||
pricesVisible: !hidePrices,
|
||||
notes: order.notes ?? undefined,
|
||||
checkRassDays: order.checkRassDays ?? undefined,
|
||||
checkRassDate: order.checkRassDate?.toISOString(),
|
||||
@@ -503,12 +565,12 @@ let OrdersService = class OrdersService {
|
||||
color: item.color,
|
||||
requestedMeterage: item.requestedMeterage,
|
||||
confirmedMeterage: item.confirmedMeterage,
|
||||
unitPrice: item.unitPrice,
|
||||
supplyPrice: item.unitPrice,
|
||||
unitDiscount: item.unitDiscount ?? 0,
|
||||
discount: item.unitDiscount ?? 0,
|
||||
lineTotal: item.lineTotal,
|
||||
totalAmount: item.lineTotal,
|
||||
unitPrice: hidePrices ? null : item.unitPrice,
|
||||
supplyPrice: hidePrices ? null : item.unitPrice,
|
||||
unitDiscount: hidePrices ? null : (item.unitDiscount ?? 0),
|
||||
discount: hidePrices ? null : (item.unitDiscount ?? 0),
|
||||
lineTotal: hidePrices ? null : item.lineTotal,
|
||||
totalAmount: hidePrices ? null : item.lineTotal,
|
||||
notes: item.notes ?? undefined,
|
||||
})),
|
||||
checks: order.checks.map((c) => ({
|
||||
@@ -573,15 +635,13 @@ let OrdersService = class OrdersService {
|
||||
changedAt: h.changedAt.toISOString(),
|
||||
})),
|
||||
};
|
||||
if (adminView) {
|
||||
return base;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
};
|
||||
exports.OrdersService = OrdersService;
|
||||
exports.OrdersService = OrdersService = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [prisma_service_1.PrismaService])
|
||||
__metadata("design:paramtypes", [prisma_service_1.PrismaService,
|
||||
notifications_service_1.NotificationsService])
|
||||
], OrdersService);
|
||||
//# sourceMappingURL=orders.service.js.map
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -4,6 +4,7 @@ export declare class UpdateSettingsDto {
|
||||
defaultSettlementCheck?: boolean;
|
||||
defaultSettlementCash?: boolean;
|
||||
smsOrderRegistered?: boolean;
|
||||
smsPricingReady?: boolean;
|
||||
smsPaymentReminder?: boolean;
|
||||
smsCheckReview?: boolean;
|
||||
smsShippingUpdate?: boolean;
|
||||
|
||||
Vendored
+6
@@ -18,6 +18,7 @@ class UpdateSettingsDto {
|
||||
defaultSettlementCheck;
|
||||
defaultSettlementCash;
|
||||
smsOrderRegistered;
|
||||
smsPricingReady;
|
||||
smsPaymentReminder;
|
||||
smsCheckReview;
|
||||
smsShippingUpdate;
|
||||
@@ -54,6 +55,11 @@ __decorate([
|
||||
(0, class_validator_1.IsBoolean)(),
|
||||
__metadata("design:type", Boolean)
|
||||
], UpdateSettingsDto.prototype, "smsOrderRegistered", void 0);
|
||||
__decorate([
|
||||
(0, class_validator_1.IsOptional)(),
|
||||
(0, class_validator_1.IsBoolean)(),
|
||||
__metadata("design:type", Boolean)
|
||||
], UpdateSettingsDto.prototype, "smsPricingReady", void 0);
|
||||
__decorate([
|
||||
(0, class_validator_1.IsOptional)(),
|
||||
(0, class_validator_1.IsBoolean)(),
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"settings.dto.js","sourceRoot":"","sources":["../../../src/settings/dto/settings.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAA8E;AAC9E,yDAAyC;AAEzC,MAAa,iBAAiB;IAK5B,qBAAqB,CAAU;IAM/B,wBAAwB,CAAU;IAIlC,sBAAsB,CAAW;IAIjC,qBAAqB,CAAW;IAIhC,kBAAkB,CAAW;IAI7B,kBAAkB,CAAW;IAI7B,cAAc,CAAW;IAIzB,iBAAiB,CAAW;IAI5B,YAAY,CAAiB;IAI7B,YAAY,CAAiB;CAC9B;AA5CD,8CA4CC;AAvCC;IAJC,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAClB,IAAA,uBAAK,GAAE;IACP,IAAA,qBAAG,EAAC,CAAC,CAAC;;gEACwB;AAM/B;IAJC,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAClB,IAAA,uBAAK,GAAE;IACP,IAAA,qBAAG,EAAC,CAAC,CAAC;;mEAC2B;AAIlC;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;iEACqB;AAIjC;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;gEACoB;AAIhC;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;6DACiB;AAI7B;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;6DACiB;AAI7B;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;yDACa;AAIzB;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;4DACgB;AAI5B;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;uDACkB;AAI7B;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;uDACkB"}
|
||||
{"version":3,"file":"settings.dto.js","sourceRoot":"","sources":["../../../src/settings/dto/settings.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAA8E;AAC9E,yDAAyC;AAEzC,MAAa,iBAAiB;IAK5B,qBAAqB,CAAU;IAM/B,wBAAwB,CAAU;IAIlC,sBAAsB,CAAW;IAIjC,qBAAqB,CAAW;IAIhC,kBAAkB,CAAW;IAI7B,eAAe,CAAW;IAI1B,kBAAkB,CAAW;IAI7B,cAAc,CAAW;IAIzB,iBAAiB,CAAW;IAI5B,YAAY,CAAiB;IAI7B,YAAY,CAAiB;CAC9B;AAhDD,8CAgDC;AA3CC;IAJC,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAClB,IAAA,uBAAK,GAAE;IACP,IAAA,qBAAG,EAAC,CAAC,CAAC;;gEACwB;AAM/B;IAJC,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAClB,IAAA,uBAAK,GAAE;IACP,IAAA,qBAAG,EAAC,CAAC,CAAC;;mEAC2B;AAIlC;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;iEACqB;AAIjC;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;gEACoB;AAIhC;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;6DACiB;AAI7B;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;0DACc;AAI1B;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;6DACiB;AAI7B;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;yDACa;AAIzB;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;4DACgB;AAI5B;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;uDACkB;AAI7B;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;uDACkB"}
|
||||
+2
@@ -10,6 +10,7 @@ export declare class SettingsController {
|
||||
defaultSettlementCheck: boolean;
|
||||
defaultSettlementCash: boolean;
|
||||
smsOrderRegistered: boolean;
|
||||
smsPricingReady: boolean;
|
||||
smsPaymentReminder: boolean;
|
||||
smsCheckReview: boolean;
|
||||
smsShippingUpdate: boolean;
|
||||
@@ -24,6 +25,7 @@ export declare class SettingsController {
|
||||
defaultSettlementCheck: boolean;
|
||||
defaultSettlementCash: boolean;
|
||||
smsOrderRegistered: boolean;
|
||||
smsPricingReady: boolean;
|
||||
smsPaymentReminder: boolean;
|
||||
smsCheckReview: boolean;
|
||||
smsShippingUpdate: boolean;
|
||||
|
||||
Vendored
+2
@@ -10,6 +10,7 @@ export declare class SettingsService {
|
||||
defaultSettlementCheck: boolean;
|
||||
defaultSettlementCash: boolean;
|
||||
smsOrderRegistered: boolean;
|
||||
smsPricingReady: boolean;
|
||||
smsPaymentReminder: boolean;
|
||||
smsCheckReview: boolean;
|
||||
smsShippingUpdate: boolean;
|
||||
@@ -24,6 +25,7 @@ export declare class SettingsService {
|
||||
defaultSettlementCheck: boolean;
|
||||
defaultSettlementCash: boolean;
|
||||
smsOrderRegistered: boolean;
|
||||
smsPricingReady: boolean;
|
||||
smsPaymentReminder: boolean;
|
||||
smsCheckReview: boolean;
|
||||
smsShippingUpdate: boolean;
|
||||
|
||||
Vendored
+2
@@ -31,6 +31,7 @@ let SettingsService = class SettingsService {
|
||||
defaultSettlementCheck: dto.defaultSettlementCheck,
|
||||
defaultSettlementCash: dto.defaultSettlementCash,
|
||||
smsOrderRegistered: dto.smsOrderRegistered,
|
||||
smsPricingReady: dto.smsPricingReady,
|
||||
smsPaymentReminder: dto.smsPaymentReminder,
|
||||
smsCheckReview: dto.smsCheckReview,
|
||||
smsShippingUpdate: dto.smsShippingUpdate,
|
||||
@@ -59,6 +60,7 @@ let SettingsService = class SettingsService {
|
||||
defaultSettlementCheck: settings.defaultSettlementCheck,
|
||||
defaultSettlementCash: settings.defaultSettlementCash,
|
||||
smsOrderRegistered: settings.smsOrderRegistered,
|
||||
smsPricingReady: settings.smsPricingReady,
|
||||
smsPaymentReminder: settings.smsPaymentReminder,
|
||||
smsCheckReview: settings.smsCheckReview,
|
||||
smsShippingUpdate: settings.smsShippingUpdate,
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"settings.service.js","sourceRoot":"","sources":["../../src/settings/settings.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,6DAAyD;AAIlD,IAAM,eAAe,GAArB,MAAM,eAAe;IACG;IAA7B,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAEtD,KAAK,CAAC,GAAG;QACP,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC7C,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAsB;QACjC,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC;YACvD,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YACxB,IAAI,EAAE;gBACJ,qBAAqB,EAAE,GAAG,CAAC,qBAAqB;gBAChD,wBAAwB,EAAE,GAAG,CAAC,wBAAwB;gBACtD,sBAAsB,EAAE,GAAG,CAAC,sBAAsB;gBAClD,qBAAqB,EAAE,GAAG,CAAC,qBAAqB;gBAChD,kBAAkB,EAAE,GAAG,CAAC,kBAAkB;gBAC1C,kBAAkB,EAAE,GAAG,CAAC,kBAAkB;gBAC1C,cAAc,EAAE,GAAG,CAAC,cAAc;gBAClC,iBAAiB,EAAE,GAAG,CAAC,iBAAiB;gBACxC,YAAY,EACV,GAAG,CAAC,YAAY,KAAK,SAAS;oBAC5B,CAAC,CAAC,SAAS;oBACX,CAAC,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,IAAI;gBACtC,YAAY,EACV,GAAG,CAAC,YAAY,KAAK,SAAS;oBAC5B,CAAC,CAAC,SAAS;oBACX,CAAC,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,IAAI;aACvC;SACF,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC;YACvC,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YACxB,MAAM,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YACzB,MAAM,EAAE,EAAE;SACX,CAAC,CAAC;IACL,CAAC;IAEO,GAAG,CAAC,QAaX;QACC,OAAO;YACL,EAAE,EAAE,QAAQ,CAAC,EAAE;YACf,qBAAqB,EAAE,QAAQ,CAAC,qBAAqB;YACrD,wBAAwB,EAAE,QAAQ,CAAC,wBAAwB;YAC3D,sBAAsB,EAAE,QAAQ,CAAC,sBAAsB;YACvD,qBAAqB,EAAE,QAAQ,CAAC,qBAAqB;YACrD,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;YAC/C,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;YAC/C,cAAc,EAAE,QAAQ,CAAC,cAAc;YACvC,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB;YAC7C,YAAY,EAAE,QAAQ,CAAC,YAAY;YACnC,YAAY,EAAE,QAAQ,CAAC,YAAY;YACnC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,WAAW,EAAE;SAC5C,CAAC;IACJ,CAAC;CACF,CAAA;AAvEY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;qCAE0B,8BAAa;GADvC,eAAe,CAuE3B"}
|
||||
{"version":3,"file":"settings.service.js","sourceRoot":"","sources":["../../src/settings/settings.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,6DAAyD;AAIlD,IAAM,eAAe,GAArB,MAAM,eAAe;IACG;IAA7B,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAEtD,KAAK,CAAC,GAAG;QACP,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC7C,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAsB;QACjC,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC;YACvD,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YACxB,IAAI,EAAE;gBACJ,qBAAqB,EAAE,GAAG,CAAC,qBAAqB;gBAChD,wBAAwB,EAAE,GAAG,CAAC,wBAAwB;gBACtD,sBAAsB,EAAE,GAAG,CAAC,sBAAsB;gBAClD,qBAAqB,EAAE,GAAG,CAAC,qBAAqB;gBAChD,kBAAkB,EAAE,GAAG,CAAC,kBAAkB;gBAC1C,eAAe,EAAE,GAAG,CAAC,eAAe;gBACpC,kBAAkB,EAAE,GAAG,CAAC,kBAAkB;gBAC1C,cAAc,EAAE,GAAG,CAAC,cAAc;gBAClC,iBAAiB,EAAE,GAAG,CAAC,iBAAiB;gBACxC,YAAY,EACV,GAAG,CAAC,YAAY,KAAK,SAAS;oBAC5B,CAAC,CAAC,SAAS;oBACX,CAAC,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,IAAI;gBACtC,YAAY,EACV,GAAG,CAAC,YAAY,KAAK,SAAS;oBAC5B,CAAC,CAAC,SAAS;oBACX,CAAC,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,IAAI;aACvC;SACF,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC;YACvC,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YACxB,MAAM,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YACzB,MAAM,EAAE,EAAE;SACX,CAAC,CAAC;IACL,CAAC;IAEO,GAAG,CAAC,QAcX;QACC,OAAO;YACL,EAAE,EAAE,QAAQ,CAAC,EAAE;YACf,qBAAqB,EAAE,QAAQ,CAAC,qBAAqB;YACrD,wBAAwB,EAAE,QAAQ,CAAC,wBAAwB;YAC3D,sBAAsB,EAAE,QAAQ,CAAC,sBAAsB;YACvD,qBAAqB,EAAE,QAAQ,CAAC,qBAAqB;YACrD,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;YAC/C,eAAe,EAAE,QAAQ,CAAC,eAAe;YACzC,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;YAC/C,cAAc,EAAE,QAAQ,CAAC,cAAc;YACvC,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB;YAC7C,YAAY,EAAE,QAAQ,CAAC,YAAY;YACnC,YAAY,EAAE,QAAQ,CAAC,YAAY;YACnC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,WAAW,EAAE;SAC5C,CAAC;IACJ,CAAC;CACF,CAAA;AA1EY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;qCAE0B,8BAAa;GADvC,eAAe,CA0E3B"}
|
||||
Vendored
+2
@@ -10,11 +10,13 @@ exports.ShipmentsModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const shipments_controller_1 = require("./shipments.controller");
|
||||
const shipments_service_1 = require("./shipments.service");
|
||||
const notifications_module_1 = require("../notifications/notifications.module");
|
||||
let ShipmentsModule = class ShipmentsModule {
|
||||
};
|
||||
exports.ShipmentsModule = ShipmentsModule;
|
||||
exports.ShipmentsModule = ShipmentsModule = __decorate([
|
||||
(0, common_1.Module)({
|
||||
imports: [notifications_module_1.NotificationsModule],
|
||||
controllers: [shipments_controller_1.ShipmentsController],
|
||||
providers: [shipments_service_1.ShipmentsService],
|
||||
exports: [shipments_service_1.ShipmentsService],
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"shipments.module.js","sourceRoot":"","sources":["../../src/shipments/shipments.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,iEAA6D;AAC7D,2DAAuD;AAOhD,IAAM,eAAe,GAArB,MAAM,eAAe;CAAG,CAAA;AAAlB,0CAAe;0BAAf,eAAe;IAL3B,IAAA,eAAM,EAAC;QACN,WAAW,EAAE,CAAC,0CAAmB,CAAC;QAClC,SAAS,EAAE,CAAC,oCAAgB,CAAC;QAC7B,OAAO,EAAE,CAAC,oCAAgB,CAAC;KAC5B,CAAC;GACW,eAAe,CAAG"}
|
||||
{"version":3,"file":"shipments.module.js","sourceRoot":"","sources":["../../src/shipments/shipments.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,iEAA6D;AAC7D,2DAAuD;AACvD,gFAA4E;AAQrE,IAAM,eAAe,GAArB,MAAM,eAAe;CAAG,CAAA;AAAlB,0CAAe;0BAAf,eAAe;IAN3B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,0CAAmB,CAAC;QAC9B,WAAW,EAAE,CAAC,0CAAmB,CAAC;QAClC,SAAS,EAAE,CAAC,oCAAgB,CAAC;QAC7B,OAAO,EAAE,CAAC,oCAAgB,CAAC;KAC5B,CAAC;GACW,eAAe,CAAG"}
|
||||
+4
-1
@@ -1,9 +1,11 @@
|
||||
import { ShipmentStatus } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { AdvanceShipmentStatusDto, CreateShipmentDto, UpdateShipmentDto } from './dto/shipment.dto';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
export declare class ShipmentsService {
|
||||
private readonly prisma;
|
||||
constructor(prisma: PrismaService);
|
||||
private readonly notifications;
|
||||
constructor(prisma: PrismaService, notifications: NotificationsService);
|
||||
findAll(status?: ShipmentStatus): Promise<{
|
||||
id: string;
|
||||
orderId: string;
|
||||
@@ -74,6 +76,7 @@ export declare class ShipmentsService {
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}>;
|
||||
private notifyOrderStatus;
|
||||
private ensure;
|
||||
private map;
|
||||
}
|
||||
|
||||
Vendored
+21
-2
@@ -14,6 +14,8 @@ const common_1 = require("@nestjs/common");
|
||||
const client_1 = require("@prisma/client");
|
||||
const prisma_service_1 = require("../prisma/prisma.service");
|
||||
const name_1 = require("../common/utils/name");
|
||||
const notifications_service_1 = require("../notifications/notifications.service");
|
||||
const notification_events_1 = require("../notifications/notification-events");
|
||||
const STATUS_FLOW = [
|
||||
client_1.ShipmentStatus.preparing,
|
||||
client_1.ShipmentStatus.ready,
|
||||
@@ -28,8 +30,10 @@ const SHIPMENT_TO_ORDER = {
|
||||
};
|
||||
let ShipmentsService = class ShipmentsService {
|
||||
prisma;
|
||||
constructor(prisma) {
|
||||
notifications;
|
||||
constructor(prisma, notifications) {
|
||||
this.prisma = prisma;
|
||||
this.notifications = notifications;
|
||||
}
|
||||
async findAll(status) {
|
||||
const shipments = await this.prisma.shipment.findMany({
|
||||
@@ -92,6 +96,10 @@ let ShipmentsService = class ShipmentsService {
|
||||
}
|
||||
return created;
|
||||
});
|
||||
this.notifyOrderStatus(order.id, SHIPMENT_TO_ORDER[status], {
|
||||
carrier: shipment.carrier,
|
||||
trackingNumber: shipment.trackingNumber,
|
||||
});
|
||||
return this.map(shipment);
|
||||
}
|
||||
async update(id, dto) {
|
||||
@@ -160,8 +168,18 @@ let ShipmentsService = class ShipmentsService {
|
||||
}
|
||||
return result;
|
||||
});
|
||||
this.notifyOrderStatus(shipment.orderId, SHIPMENT_TO_ORDER[nextStatus], {
|
||||
carrier: updated.carrier,
|
||||
trackingNumber: updated.trackingNumber,
|
||||
});
|
||||
return this.map(updated);
|
||||
}
|
||||
notifyOrderStatus(orderId, status, context) {
|
||||
const event = (0, notification_events_1.eventForStatus)(status);
|
||||
if (event) {
|
||||
this.notifications.dispatchOrderEvent(orderId, event, context);
|
||||
}
|
||||
}
|
||||
async ensure(id) {
|
||||
const shipment = await this.prisma.shipment.findUnique({ where: { id } });
|
||||
if (!shipment)
|
||||
@@ -188,6 +206,7 @@ let ShipmentsService = class ShipmentsService {
|
||||
exports.ShipmentsService = ShipmentsService;
|
||||
exports.ShipmentsService = ShipmentsService = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [prisma_service_1.PrismaService])
|
||||
__metadata("design:paramtypes", [prisma_service_1.PrismaService,
|
||||
notifications_service_1.NotificationsService])
|
||||
], ShipmentsService);
|
||||
//# sourceMappingURL=shipments.service.js.map
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Generated
+46
@@ -15,6 +15,7 @@
|
||||
"@nestjs/jwt": "^11.0.2",
|
||||
"@nestjs/passport": "^11.0.5",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/schedule": "^6.1.3",
|
||||
"@nestjs/swagger": "^11.4.6",
|
||||
"@prisma/client": "^6.19.3",
|
||||
"bcryptjs": "^3.0.3",
|
||||
@@ -2452,6 +2453,19 @@
|
||||
"@nestjs/core": "^11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/schedule": {
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/@nestjs/schedule/-/schedule-6.1.3.tgz",
|
||||
"integrity": "sha512-RflMFOpR16Dwd1jAUbeB4mfGTCh65fvEdL4mSjQPJChpkRGRjIXjb+6YQcK2faQrVT60c9DmLmoVR7/ONCtuYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cron": "4.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
||||
"@nestjs/core": "^10.0.0 || ^11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/schematics": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.1.0.tgz",
|
||||
@@ -3073,6 +3087,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/luxon": {
|
||||
"version": "3.7.4",
|
||||
"resolved": "https://registry.npmmirror.com/@types/luxon/-/luxon-3.7.4.tgz",
|
||||
"integrity": "sha512-V536ZAd6ZJztrrBlLcDFaaZrXNAL2E5uGmssWf/dpSiLkmkLScXUYhUBnWPmtW+cIqnNHzf6//TCMpIc9SCRRQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/methods": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz",
|
||||
@@ -5146,6 +5166,23 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cron": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/cron/-/cron-4.4.0.tgz",
|
||||
"integrity": "sha512-fkdfq+b+AHI4cKdhZlppHveI/mgz2qpiYxcm+t5E5TsxX7QrLS1VE0+7GENEk9z0EeGPcpSciGv6ez24duWhwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/luxon": "~3.7.0",
|
||||
"luxon": "~3.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.x"
|
||||
},
|
||||
"funding": {
|
||||
"type": "ko-fi",
|
||||
"url": "https://ko-fi.com/intcreator"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@@ -7979,6 +8016,15 @@
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/luxon": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/luxon/-/luxon-3.7.2.tgz",
|
||||
"integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.17",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"@nestjs/jwt": "^11.0.2",
|
||||
"@nestjs/passport": "^11.0.5",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/schedule": "^6.1.3",
|
||||
"@nestjs/swagger": "^11.4.6",
|
||||
"@prisma/client": "^6.19.3",
|
||||
"bcryptjs": "^3.0.3",
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
-- روش تسویه دیگر در لحظه ثبت سفارش گرفته نمیشود؛ مشتری بعد از قیمتگذاری انتخاب میکند.
|
||||
ALTER TABLE "Order" ALTER COLUMN "settlementType" DROP NOT NULL;
|
||||
@@ -0,0 +1,24 @@
|
||||
-- اعلان درونبرنامهای مشتری بههمراه سوئیچ پیامک «فاکتور آماده شد»
|
||||
ALTER TABLE "SystemSettings" ADD COLUMN "smsPricingReady" BOOLEAN NOT NULL DEFAULT true;
|
||||
|
||||
CREATE TABLE "CustomerNotification" (
|
||||
"id" TEXT NOT NULL,
|
||||
"customerId" TEXT NOT NULL,
|
||||
"orderId" TEXT,
|
||||
"event" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"body" TEXT NOT NULL,
|
||||
"tone" "NotificationTone" DEFAULT 'info',
|
||||
"link" TEXT,
|
||||
"isRead" BOOLEAN NOT NULL DEFAULT false,
|
||||
"smsSent" BOOLEAN NOT NULL DEFAULT false,
|
||||
"sentAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "CustomerNotification_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE INDEX "CustomerNotification_customerId_isRead_idx" ON "CustomerNotification"("customerId", "isRead");
|
||||
CREATE INDEX "CustomerNotification_sentAt_idx" ON "CustomerNotification"("sentAt");
|
||||
|
||||
ALTER TABLE "CustomerNotification" ADD CONSTRAINT "CustomerNotification_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "CustomerNotification" ADD CONSTRAINT "CustomerNotification_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- سقف متراژ شرکت اختیاری میشود؛ خالی یعنی از SystemSettings ارث میبرد.
|
||||
ALTER TABLE "Company" ALTER COLUMN "maxMeteragePerInvoice" DROP NOT NULL;
|
||||
ALTER TABLE "Company" ALTER COLUMN "maxMeteragePerInvoice" DROP DEFAULT;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- یادآوری پایان مهلت پرداخت فقط یکبار برای هر فاکتور ارسال میشود.
|
||||
ALTER TABLE "Order" ADD COLUMN "cashReminderSentAt" TIMESTAMP(3);
|
||||
|
||||
CREATE INDEX "Order_status_cashInvoiceExpiresAt_idx" ON "Order"("status", "cashInvoiceExpiresAt");
|
||||
+35
-7
@@ -94,7 +94,8 @@ model Company {
|
||||
logoUrl String?
|
||||
isActive Boolean @default(true)
|
||||
displayOrder Int @default(0)
|
||||
maxMeteragePerInvoice Int @default(5000)
|
||||
/// خالی یعنی از تنظیمات سیستم ارث میبرد
|
||||
maxMeteragePerInvoice Int?
|
||||
cashInvoiceValidityHours Int @default(8)
|
||||
cashInvoiceValidUntilHour Int?
|
||||
createdAt DateTime @default(now())
|
||||
@@ -183,9 +184,10 @@ model Customer {
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
company Company? @relation(fields: [companyId], references: [id], onDelete: SetNull)
|
||||
carts Cart[]
|
||||
orders Order[]
|
||||
company Company? @relation(fields: [companyId], references: [id], onDelete: SetNull)
|
||||
carts Cart[]
|
||||
orders Order[]
|
||||
notifications CustomerNotification[]
|
||||
|
||||
@@index([companyId])
|
||||
}
|
||||
@@ -258,7 +260,8 @@ model Order {
|
||||
userId String
|
||||
companyId String
|
||||
companyName String
|
||||
settlementType SettlementType
|
||||
// روش تسویه بعد از قیمتگذاری ادمین توسط مشتری انتخاب میشود
|
||||
settlementType SettlementType?
|
||||
checkMode CheckRegistrationMode?
|
||||
status OrderStatus @default(awaiting_review)
|
||||
totalMeterage Float @default(0)
|
||||
@@ -270,21 +273,25 @@ model Order {
|
||||
checkRassDays Int?
|
||||
checkRassDate DateTime?
|
||||
cashInvoiceExpiresAt DateTime?
|
||||
/// زمان ارسال یادآوری پایان مهلت پرداخت تا دوباره ارسال نشود
|
||||
cashReminderSentAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
user Customer @relation(fields: [userId], references: [id], onDelete: Restrict)
|
||||
company Company @relation(fields: [companyId], references: [id], onDelete: Restrict)
|
||||
user Customer @relation(fields: [userId], references: [id], onDelete: Restrict)
|
||||
company Company @relation(fields: [companyId], references: [id], onDelete: Restrict)
|
||||
items OrderItem[]
|
||||
checks Check[]
|
||||
cashReceipt CashPaymentReceipt?
|
||||
shipment Shipment?
|
||||
statusHistory OrderStatusHistory[]
|
||||
notifications CustomerNotification[]
|
||||
|
||||
@@index([userId])
|
||||
@@index([companyId])
|
||||
@@index([status])
|
||||
@@index([createdAt])
|
||||
@@index([status, cashInvoiceExpiresAt])
|
||||
}
|
||||
|
||||
model OrderItem {
|
||||
@@ -393,6 +400,7 @@ model SystemSettings {
|
||||
defaultSettlementCheck Boolean @default(true)
|
||||
defaultSettlementCash Boolean @default(true)
|
||||
smsOrderRegistered Boolean @default(true)
|
||||
smsPricingReady Boolean @default(true)
|
||||
smsPaymentReminder Boolean @default(true)
|
||||
smsCheckReview Boolean @default(true)
|
||||
smsShippingUpdate Boolean @default(true)
|
||||
@@ -411,6 +419,26 @@ model AdminNotification {
|
||||
sentAt DateTime @default(now())
|
||||
}
|
||||
|
||||
model CustomerNotification {
|
||||
id String @id @default(cuid())
|
||||
customerId String
|
||||
orderId String?
|
||||
event String
|
||||
title String
|
||||
body String
|
||||
tone NotificationTone? @default(info)
|
||||
link String?
|
||||
isRead Boolean @default(false)
|
||||
smsSent Boolean @default(false)
|
||||
sentAt DateTime @default(now())
|
||||
|
||||
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
|
||||
order Order? @relation(fields: [orderId], references: [id], onDelete: SetNull)
|
||||
|
||||
@@index([customerId, isRead])
|
||||
@@index([sentAt])
|
||||
}
|
||||
|
||||
model InternalMessage {
|
||||
id String @id @default(cuid())
|
||||
senderName String
|
||||
|
||||
+2
-2
@@ -91,7 +91,7 @@ async function main() {
|
||||
description: 'تولیدکننده پارچههای باکیفیت',
|
||||
isActive: true,
|
||||
displayOrder: 1,
|
||||
maxMeteragePerInvoice: 5000,
|
||||
maxMeteragePerInvoice: null,
|
||||
cashInvoiceValidityHours: 8,
|
||||
cashInvoiceValidUntilHour: 17,
|
||||
},
|
||||
@@ -102,7 +102,7 @@ async function main() {
|
||||
description: 'عرضه پارچههای تخصصی',
|
||||
isActive: true,
|
||||
displayOrder: 2,
|
||||
maxMeteragePerInvoice: 5000,
|
||||
maxMeteragePerInvoice: null,
|
||||
cashInvoiceValidityHours: 8,
|
||||
cashInvoiceValidUntilHour: 17,
|
||||
},
|
||||
|
||||
+308
-113
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* تست دود جریان کامل سفارش: ورود مشتری، سبد، ثبت چکی و نقدی،
|
||||
* قیمتگذاری ادمین، رسید نقدی و بررسی وضعیتها.
|
||||
* تست دود گردش کامل سفارش مطابق روال کارفرما:
|
||||
* مشتری بدون دیدن قیمت سفارش میدهد، ادمین قیمت میگذارد و فاکتور میفرستد،
|
||||
* مشتری روش تسویه را انتخاب میکند و مسیر نقدی/چکی تا تایید نهایی پیش میرود.
|
||||
* اجرا: node scripts/smoke-order-flow.mjs
|
||||
*/
|
||||
const BASE = process.env.SMOKE_BASE_URL || 'http://127.0.0.1:3999/api';
|
||||
@@ -41,9 +42,8 @@ async function api(path, { method = 'GET', body, token } = {}) {
|
||||
return { status: res.status, data };
|
||||
}
|
||||
|
||||
function jalaliIsoInDays(days) {
|
||||
const d = new Date(Date.now() + days * 86400000);
|
||||
return d.toISOString().slice(0, 10);
|
||||
function isoInDays(days) {
|
||||
return new Date(Date.now() + days * 86400000).toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
/** تصویر base64 بزرگتر از سقف پیشفرض ۱۰۰KB اکسپرس */
|
||||
@@ -51,6 +51,22 @@ function bigBase64(kb) {
|
||||
return `data:image/jpeg;base64,${'A'.repeat(kb * 1024)}`;
|
||||
}
|
||||
|
||||
function checkPayload(amount, days, extra = {}) {
|
||||
return {
|
||||
amount,
|
||||
dueDate: isoInDays(days),
|
||||
sayadId: '1234567890123456',
|
||||
checkNumber: '123456',
|
||||
ownerNationalId: '0012345678',
|
||||
ownerFirstName: 'علی',
|
||||
ownerLastName: 'رضایی',
|
||||
bankName: 'ملی',
|
||||
branchName: 'مرکزی',
|
||||
mode: 'full',
|
||||
...extra,
|
||||
};
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log(`\n== Smoke test @ ${BASE} ==\n`);
|
||||
|
||||
@@ -65,7 +81,7 @@ async function main() {
|
||||
|
||||
const badVerify = await api('/auth/otp/verify', {
|
||||
method: 'POST',
|
||||
body: { phone: CUSTOMER_PHONE, code: '0000' === code ? '1111' : '0000' },
|
||||
body: { phone: CUSTOMER_PHONE, code: String(code) === '0000' ? '1111' : '0000' },
|
||||
});
|
||||
check('کد اشتباه رد میشود', badVerify.status === 401, `status=${badVerify.status}`);
|
||||
|
||||
@@ -91,10 +107,15 @@ async function main() {
|
||||
const noToken = await api('/orders');
|
||||
check('بدون توکن ۴۰۱', noToken.status === 401, `status=${noToken.status}`);
|
||||
|
||||
console.log('\n[3] سبد خرید');
|
||||
console.log('\n[3] سبد خرید بدون قیمت');
|
||||
const companies = await api('/companies/public');
|
||||
const company = companies.data?.[0];
|
||||
check('لیست شرکتها', !!company, JSON.stringify(companies.data).slice(0, 120));
|
||||
check(
|
||||
'سقف متراژ موثر برگردانده میشود',
|
||||
typeof company?.maxMeteragePerInvoice === 'number' && company.maxMeteragePerInvoice > 0,
|
||||
`max=${company?.maxMeteragePerInvoice}`,
|
||||
);
|
||||
|
||||
const products = await api(`/products?companyId=${company.id}`, { token: userToken });
|
||||
const product = (Array.isArray(products.data) ? products.data : products.data?.items)?.find(
|
||||
@@ -110,89 +131,63 @@ async function main() {
|
||||
body: { productId: product.id, meterage },
|
||||
});
|
||||
check('افزودن به سبد', addItem.status < 300, `status=${addItem.status} ${JSON.stringify(addItem.data).slice(0, 120)}`);
|
||||
const cartItem = addItem.data?.items?.[0];
|
||||
check('سبد قیمت واحد ندارد', cartItem?.unitPrice === undefined, `unitPrice=${cartItem?.unitPrice}`);
|
||||
|
||||
console.log('\n[4] ثبت سفارش چکی با تصویر بزرگ');
|
||||
const checkOrder = await api('/orders', {
|
||||
method: 'POST',
|
||||
token: userToken,
|
||||
body: {
|
||||
companyId: company.id,
|
||||
settlementType: 'check',
|
||||
checkMode: 'full',
|
||||
items: [{ productId: product.id, meterage }],
|
||||
checks: [
|
||||
{
|
||||
amount: 2000000,
|
||||
dueDate: jalaliIsoInDays(30),
|
||||
sayadId: '1234567890123456',
|
||||
checkNumber: '123456',
|
||||
ownerNationalId: '0012345678',
|
||||
ownerFirstName: 'علی',
|
||||
ownerLastName: 'رضایی',
|
||||
bankName: 'ملی',
|
||||
branchName: 'مرکزی',
|
||||
frontImage: bigBase64(300),
|
||||
backImage: bigBase64(300),
|
||||
mode: 'full',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
check(
|
||||
'ثبت سفارش چکی (بدنه ~۶۰۰KB)',
|
||||
checkOrder.status < 300,
|
||||
`status=${checkOrder.status} ${JSON.stringify(checkOrder.data).slice(0, 200)}`,
|
||||
);
|
||||
const checkOrderId = checkOrder.data?.id;
|
||||
check('راس چک محاسبه شد', checkOrder.data?.checkRassDays === 30, `rass=${checkOrder.data?.checkRassDays}`);
|
||||
|
||||
console.log('\n[5] فیلدهای اضافه باید رد شوند');
|
||||
const extraField = await api('/orders', {
|
||||
method: 'POST',
|
||||
token: userToken,
|
||||
body: {
|
||||
companyId: company.id,
|
||||
settlementType: 'cash',
|
||||
items: [{ productId: product.id, meterage }],
|
||||
bogusField: 'x',
|
||||
},
|
||||
});
|
||||
check('فیلد ناشناخته ۴۰۰ میدهد', extraField.status === 400, `status=${extraField.status}`);
|
||||
|
||||
console.log('\n[6] ثبت سفارش نقدی');
|
||||
await api('/carts/items', {
|
||||
method: 'POST',
|
||||
token: userToken,
|
||||
body: { productId: product.id, meterage },
|
||||
});
|
||||
console.log('\n[4] ثبت سفارش بدون روش تسویه و بدون قیمت');
|
||||
const cashOrder = await api('/orders', {
|
||||
method: 'POST',
|
||||
token: userToken,
|
||||
body: { companyId: company.id, items: [{ productId: product.id, meterage }] },
|
||||
});
|
||||
check('ثبت سفارش', cashOrder.status < 300, `status=${cashOrder.status} ${JSON.stringify(cashOrder.data).slice(0, 200)}`);
|
||||
const cashOrderId = cashOrder.data?.id;
|
||||
check('وضعیت اولیه در انتظار بررسی', cashOrder.data?.status === 'awaiting_review', `status=${cashOrder.data?.status}`);
|
||||
check('روش تسویه خالی است', cashOrder.data?.settlementType == null, `settlement=${cashOrder.data?.settlementType}`);
|
||||
check('مبلغ برای مشتری پنهان است', cashOrder.data?.finalAmount == null && cashOrder.data?.totalAmount == null);
|
||||
check('پرچم pricesVisible خاموش است', cashOrder.data?.pricesVisible === false, `flag=${cashOrder.data?.pricesVisible}`);
|
||||
check(
|
||||
'قیمت واحد آیتمها پنهان است',
|
||||
(cashOrder.data?.items ?? []).every((i) => i.unitPrice == null),
|
||||
);
|
||||
|
||||
console.log('\n[5] اعتبارسنجی ورودی');
|
||||
const extraField = await api('/orders', {
|
||||
method: 'POST',
|
||||
token: userToken,
|
||||
body: { companyId: company.id, items: [{ productId: product.id, meterage }], bogusField: 'x' },
|
||||
});
|
||||
check('فیلد ناشناخته ۴۰۰ میدهد', extraField.status === 400, `status=${extraField.status}`);
|
||||
|
||||
const earlySettlement = await api(`/orders/${cashOrderId}/settlement`, {
|
||||
method: 'POST',
|
||||
token: userToken,
|
||||
body: { settlementType: 'cash' },
|
||||
});
|
||||
check('انتخاب تسویه قبل از فاکتور رد میشود', earlySettlement.status === 400, `status=${earlySettlement.status}`);
|
||||
|
||||
const earlyReceipt = await api(`/orders/${cashOrderId}/cash-receipt`, {
|
||||
method: 'POST',
|
||||
token: userToken,
|
||||
body: {
|
||||
companyId: company.id,
|
||||
settlementType: 'cash',
|
||||
items: [{ productId: product.id, meterage }],
|
||||
amount: 1000,
|
||||
transferDate: isoInDays(0),
|
||||
transferTime: '12:00',
|
||||
trackingNumber: '1',
|
||||
destinationAccountId: 'x',
|
||||
},
|
||||
});
|
||||
check('ثبت سفارش نقدی', cashOrder.status < 300, `status=${cashOrder.status} ${JSON.stringify(cashOrder.data).slice(0, 200)}`);
|
||||
const cashOrderId = cashOrder.data?.id;
|
||||
const expiry = cashOrder.data?.cashInvoiceExpiresAt;
|
||||
check('انقضای فاکتور در آینده است', !!expiry && new Date(expiry) > new Date(), `expiry=${expiry}`);
|
||||
check('رسید قبل از فاکتور رد میشود', earlyReceipt.status === 400, `status=${earlyReceipt.status}`);
|
||||
|
||||
console.log('\n[7] سفارش در لیست مشتری دیده میشود');
|
||||
const myOrders = await api('/orders', { token: userToken });
|
||||
const ids = (myOrders.data ?? []).map((o) => o.id);
|
||||
check('سفارش چکی در لیست', ids.includes(checkOrderId));
|
||||
check('سفارش نقدی در لیست', ids.includes(cashOrderId));
|
||||
|
||||
console.log('\n[8] لیست و فیلتر ادمین');
|
||||
console.log('\n[6] سطلهای وضعیت ادمین');
|
||||
const adminOrders = await api('/admin/orders', { token: adminToken });
|
||||
const adminIds = (adminOrders.data ?? []).map((o) => o.id);
|
||||
check('هر دو سفارش برای ادمین', adminIds.includes(checkOrderId) && adminIds.includes(cashOrderId));
|
||||
const adminStatuses = new Set((adminOrders.data ?? []).map((o) => o.adminStatus));
|
||||
check('سفارش برای ادمین دیده میشود', adminIds.includes(cashOrderId));
|
||||
const allowedTabs = new Set([
|
||||
'pending_review',
|
||||
'awaiting_customer',
|
||||
'pending_check',
|
||||
'pending_payment',
|
||||
'needs_correction',
|
||||
'check_correction',
|
||||
'final_approval',
|
||||
@@ -202,20 +197,35 @@ async function main() {
|
||||
'rejected',
|
||||
'expired',
|
||||
]);
|
||||
const unknown = [...adminStatuses].filter((s) => !allowedTabs.has(s));
|
||||
const unknown = [...new Set((adminOrders.data ?? []).map((o) => o.adminStatus))].filter(
|
||||
(s) => !allowedTabs.has(s),
|
||||
);
|
||||
check('همه وضعیتها به تب معتبر نگاشت شدهاند', unknown.length === 0, `unknown=${unknown.join(',')}`);
|
||||
|
||||
const filtered = await api('/admin/orders?status=pending_review', { token: adminToken });
|
||||
check(
|
||||
'فیلتر «در انتظار بررسی» سفارشها را برمیگرداند',
|
||||
'فیلتر «در انتظار بررسی» سفارش را برمیگرداند',
|
||||
(filtered.data ?? []).some((o) => o.id === cashOrderId),
|
||||
`count=${(filtered.data ?? []).length}`,
|
||||
);
|
||||
|
||||
console.log('\n[9] قیمتگذاری ادمین');
|
||||
console.log('\n[7] قیمتگذاری ادمین');
|
||||
const detail = await api(`/admin/orders/${cashOrderId}`, { token: adminToken });
|
||||
const items = detail.data?.items ?? [];
|
||||
check('سفارش آیتم دارد', items.length > 0);
|
||||
check(
|
||||
'ادمین گذرهای مجاز را میبیند',
|
||||
Array.isArray(detail.data?.allowedTransitions) && detail.data.allowedTransitions.includes('pricing'),
|
||||
`transitions=${JSON.stringify(detail.data?.allowedTransitions)}`,
|
||||
);
|
||||
|
||||
const earlyInvoice = await api(`/admin/orders/${cashOrderId}/status`, {
|
||||
method: 'PATCH',
|
||||
token: adminToken,
|
||||
body: { status: 'awaiting_settlement' },
|
||||
});
|
||||
check('ارسال فاکتور بدون قیمتگذاری رد میشود', earlyInvoice.status === 400, `status=${earlyInvoice.status}`);
|
||||
|
||||
const pricing = await api(`/admin/orders/${cashOrderId}/pricing`, {
|
||||
method: 'PATCH',
|
||||
token: adminToken,
|
||||
@@ -225,17 +235,13 @@ async function main() {
|
||||
},
|
||||
});
|
||||
const expectedTotal = 100000 * meterage;
|
||||
const expectedFinal = expectedTotal + 50000;
|
||||
check('قیمتگذاری موفق', pricing.status < 300, `status=${pricing.status} ${JSON.stringify(pricing.data).slice(0, 150)}`);
|
||||
check(
|
||||
'جمع کل درست است',
|
||||
Math.abs((pricing.data?.totalAmount ?? 0) - expectedTotal) < 0.01,
|
||||
`total=${pricing.data?.totalAmount} expected=${expectedTotal}`,
|
||||
);
|
||||
check(
|
||||
'مبلغ نهایی = جمع + حمل',
|
||||
Math.abs((pricing.data?.finalAmount ?? 0) - (expectedTotal + 50000)) < 0.01,
|
||||
`final=${pricing.data?.finalAmount}`,
|
||||
);
|
||||
check('جمع کل درست است', Math.abs((pricing.data?.totalAmount ?? 0) - expectedTotal) < 0.01, `total=${pricing.data?.totalAmount}`);
|
||||
check('مبلغ نهایی = جمع + حمل', Math.abs((pricing.data?.finalAmount ?? 0) - expectedFinal) < 0.01, `final=${pricing.data?.finalAmount}`);
|
||||
|
||||
const stillHidden = await api(`/orders/${cashOrderId}`, { token: userToken });
|
||||
check('قیمت تا ارسال فاکتور برای مشتری پنهان است', stillHidden.data?.finalAmount == null, `final=${stillHidden.data?.finalAmount}`);
|
||||
|
||||
const badDiscount = await api(`/admin/orders/${cashOrderId}/pricing`, {
|
||||
method: 'PATCH',
|
||||
@@ -244,7 +250,64 @@ async function main() {
|
||||
});
|
||||
check('تخفیف بیشتر از قیمت رد میشود', badDiscount.status === 400, `status=${badDiscount.status}`);
|
||||
|
||||
console.log('\n[10] رسید پرداخت نقدی');
|
||||
console.log('\n[8] ارسال فاکتور برای مشتری');
|
||||
await api(`/admin/orders/${cashOrderId}/pricing`, {
|
||||
method: 'PATCH',
|
||||
token: adminToken,
|
||||
body: {
|
||||
items: [{ id: items[0].id, confirmedMeterage: meterage, unitPrice: 100000, unitDiscount: 0 }],
|
||||
shippingCost: 50000,
|
||||
},
|
||||
});
|
||||
const invoice = await api(`/admin/orders/${cashOrderId}/status`, {
|
||||
method: 'PATCH',
|
||||
token: adminToken,
|
||||
body: { status: 'awaiting_settlement', note: 'فاکتور نهایی شد' },
|
||||
});
|
||||
check('ارسال فاکتور', invoice.status < 300, `status=${invoice.status} ${JSON.stringify(invoice.data).slice(0, 150)}`);
|
||||
|
||||
const priced = await api(`/orders/${cashOrderId}`, { token: userToken });
|
||||
check('مشتری مبلغ فاکتور را میبیند', Math.abs((priced.data?.finalAmount ?? 0) - expectedFinal) < 0.01, `final=${priced.data?.finalAmount}`);
|
||||
check('pricesVisible روشن شد', priced.data?.pricesVisible !== false);
|
||||
|
||||
const illegal = await api(`/admin/orders/${cashOrderId}/status`, {
|
||||
method: 'PATCH',
|
||||
token: adminToken,
|
||||
body: { status: 'delivered' },
|
||||
});
|
||||
check('گذر غیرمجاز بدون force رد میشود', illegal.status === 400, `status=${illegal.status}`);
|
||||
const forceNoReason = await api(`/admin/orders/${cashOrderId}/status`, {
|
||||
method: 'PATCH',
|
||||
token: adminToken,
|
||||
body: { status: 'delivered', force: true },
|
||||
});
|
||||
check('force بدون دلیل رد میشود', forceNoReason.status === 400, `status=${forceNoReason.status}`);
|
||||
|
||||
console.log('\n[9] اعلان مشتری');
|
||||
const notifs = await api('/notifications', { token: userToken });
|
||||
const invoiceNotif = (notifs.data ?? []).find((n) => n.event === 'invoice_priced');
|
||||
check('اعلان صدور فاکتور ساخته شد', !!invoiceNotif, JSON.stringify(notifs.data)?.slice(0, 200));
|
||||
check('اعلان به سفارش لینک دارد', invoiceNotif?.link === `/orders/${cashOrderId}`, `link=${invoiceNotif?.link}`);
|
||||
const unread = await api('/notifications/unread-count', { token: userToken });
|
||||
check('شمارنده خواندهنشده مثبت است', (unread.data?.count ?? 0) > 0, `count=${unread.data?.count}`);
|
||||
if (invoiceNotif) {
|
||||
const marked = await api(`/notifications/${invoiceNotif.id}/read`, { method: 'PATCH', token: userToken });
|
||||
check('علامت خواندهشده', marked.status < 300 && marked.data?.count < (unread.data?.count ?? 0), JSON.stringify(marked.data));
|
||||
}
|
||||
const readAll = await api('/notifications/read-all', { method: 'PATCH', token: userToken });
|
||||
check('خواندن همه', readAll.status < 300 && (readAll.data?.count ?? -1) === 0, JSON.stringify(readAll.data));
|
||||
|
||||
console.log('\n[10] مسیر نقدی');
|
||||
const chooseCash = await api(`/orders/${cashOrderId}/settlement`, {
|
||||
method: 'POST',
|
||||
token: userToken,
|
||||
body: { settlementType: 'cash' },
|
||||
});
|
||||
check('انتخاب تسویه نقدی', chooseCash.status < 300, `status=${chooseCash.status} ${JSON.stringify(chooseCash.data).slice(0, 150)}`);
|
||||
check('وضعیت در انتظار پرداخت', chooseCash.data?.status === 'awaiting_cash_payment', `status=${chooseCash.data?.status}`);
|
||||
const expiry = chooseCash.data?.cashInvoiceExpiresAt;
|
||||
check('انقضای فاکتور در آینده است', !!expiry && new Date(expiry) > new Date(), `expiry=${expiry}`);
|
||||
|
||||
const accounts = await api(`/companies/${company.id}/bank-accounts`, { token: userToken });
|
||||
const account = (accounts.data ?? [])[0];
|
||||
check('حساب بانکی موجود', !!account, JSON.stringify(accounts.data).slice(0, 120));
|
||||
@@ -253,8 +316,8 @@ async function main() {
|
||||
method: 'POST',
|
||||
token: userToken,
|
||||
body: {
|
||||
amount: expectedTotal + 50000,
|
||||
transferDate: jalaliIsoInDays(0),
|
||||
amount: expectedFinal,
|
||||
transferDate: isoInDays(0),
|
||||
transferTime: '12:00',
|
||||
trackingNumber: '987654321',
|
||||
destinationAccountId: account.id,
|
||||
@@ -262,15 +325,151 @@ async function main() {
|
||||
},
|
||||
});
|
||||
check('ثبت رسید (تصویر ۴۰۰KB)', receipt.status < 300, `status=${receipt.status} ${JSON.stringify(receipt.data).slice(0, 200)}`);
|
||||
|
||||
const afterReceipt = await api(`/orders/${cashOrderId}`, { token: userToken });
|
||||
check('وضعیت به تأیید پرداخت رفت', afterReceipt.data?.status === 'awaiting_cash_confirmation', `status=${afterReceipt.data?.status}`);
|
||||
|
||||
const receipts = await api('/admin/cash-payments', { token: adminToken });
|
||||
const pendingReceipt = (receipts.data ?? []).find((r) => r.orderId === cashOrderId);
|
||||
check('رسید در صف ادمین است', !!pendingReceipt, JSON.stringify(receipts.data)?.slice(0, 160));
|
||||
|
||||
const rejectReceipt = await api(`/admin/cash-payments/${pendingReceipt.id}/status`, {
|
||||
method: 'PATCH',
|
||||
token: adminToken,
|
||||
body: { status: 'rejected', note: 'مبلغ نامطابق' },
|
||||
});
|
||||
check('رد رسید', rejectReceipt.status < 300, `status=${rejectReceipt.status}`);
|
||||
const afterReject = await api(`/orders/${cashOrderId}`, { token: userToken });
|
||||
check(
|
||||
'وضعیت به تأیید پرداخت رفت',
|
||||
afterReceipt.data?.status === 'awaiting_cash_confirmation',
|
||||
`status=${afterReceipt.data?.status}`,
|
||||
'رد رسید مشتری را به مرحله پرداخت برمیگرداند',
|
||||
afterReject.data?.status === 'awaiting_cash_payment',
|
||||
`status=${afterReject.data?.status}`,
|
||||
);
|
||||
|
||||
console.log('\n[11] دسترسی متقاطع بین مشتریها');
|
||||
const approveReceipt = await api(`/admin/cash-payments/${pendingReceipt.id}/status`, {
|
||||
method: 'PATCH',
|
||||
token: adminToken,
|
||||
body: { status: 'approved' },
|
||||
});
|
||||
check('تأیید رسید', approveReceipt.status < 300, `status=${approveReceipt.status}`);
|
||||
const afterApprove = await api(`/orders/${cashOrderId}`, { token: userToken });
|
||||
check('وضعیت پرداخت تأییدشده', afterApprove.data?.status === 'cash_confirmed', `status=${afterApprove.data?.status}`);
|
||||
|
||||
const finalApprove = await api(`/admin/orders/${cashOrderId}/status`, {
|
||||
method: 'PATCH',
|
||||
token: adminToken,
|
||||
body: { status: 'final_approved' },
|
||||
});
|
||||
check('تأیید نهایی سفارش نقدی', finalApprove.status < 300, `status=${finalApprove.status}`);
|
||||
|
||||
console.log('\n[11] مسیر چکی با حلقه اصلاح');
|
||||
await api('/carts', { method: 'DELETE', token: userToken });
|
||||
await api('/carts/items', {
|
||||
method: 'POST',
|
||||
token: userToken,
|
||||
body: { productId: product.id, meterage },
|
||||
});
|
||||
const checkOrder = await api('/orders', {
|
||||
method: 'POST',
|
||||
token: userToken,
|
||||
body: { companyId: company.id, items: [{ productId: product.id, meterage }] },
|
||||
});
|
||||
const checkOrderId = checkOrder.data?.id;
|
||||
check('ثبت سفارش دوم', checkOrder.status < 300, `status=${checkOrder.status}`);
|
||||
|
||||
const checkDetail = await api(`/admin/orders/${checkOrderId}`, { token: adminToken });
|
||||
const checkItem = checkDetail.data?.items?.[0];
|
||||
await api(`/admin/orders/${checkOrderId}/pricing`, {
|
||||
method: 'PATCH',
|
||||
token: adminToken,
|
||||
body: { items: [{ id: checkItem.id, confirmedMeterage: meterage, unitPrice: 100000 }] },
|
||||
});
|
||||
await api(`/admin/orders/${checkOrderId}/status`, {
|
||||
method: 'PATCH',
|
||||
token: adminToken,
|
||||
body: { status: 'awaiting_settlement' },
|
||||
});
|
||||
const chooseCheck = await api(`/orders/${checkOrderId}/settlement`, {
|
||||
method: 'POST',
|
||||
token: userToken,
|
||||
body: { settlementType: 'check', checkMode: 'full' },
|
||||
});
|
||||
check('انتخاب تسویه چکی', chooseCheck.status < 300, `status=${chooseCheck.status} ${JSON.stringify(chooseCheck.data).slice(0, 150)}`);
|
||||
check('وضعیت در حال ثبت چک', chooseCheck.data?.status === 'check_in_progress', `status=${chooseCheck.data?.status}`);
|
||||
|
||||
const submitChecks = await api(`/orders/${checkOrderId}/checks`, {
|
||||
method: 'PATCH',
|
||||
token: userToken,
|
||||
body: {
|
||||
checkMode: 'full',
|
||||
checks: [
|
||||
checkPayload(expectedTotal, 30, {
|
||||
frontImage: bigBase64(300),
|
||||
backImage: bigBase64(300),
|
||||
}),
|
||||
],
|
||||
},
|
||||
});
|
||||
check('ارسال چکها (بدنه ~۶۰۰KB)', submitChecks.status < 300, `status=${submitChecks.status} ${JSON.stringify(submitChecks.data).slice(0, 200)}`);
|
||||
check('وضعیت بررسی چک', submitChecks.data?.status === 'awaiting_check_review', `status=${submitChecks.data?.status}`);
|
||||
check('راس چک محاسبه شد', submitChecks.data?.checkRassDays === 30, `rass=${submitChecks.data?.checkRassDays}`);
|
||||
|
||||
const orderChecks = await api(`/admin/orders/${checkOrderId}/checks`, { token: adminToken });
|
||||
const firstCheck = (orderChecks.data ?? [])[0];
|
||||
check('چک برای ادمین دیده میشود', !!firstCheck, JSON.stringify(orderChecks.data)?.slice(0, 160));
|
||||
|
||||
const rejectCheck = await api(`/admin/checks/${firstCheck.id}/status`, {
|
||||
method: 'PATCH',
|
||||
token: adminToken,
|
||||
body: { status: 'rejected', note: 'تصویر ناخوانا' },
|
||||
});
|
||||
check('رد چک', rejectCheck.status < 300, `status=${rejectCheck.status}`);
|
||||
const afterCheckReject = await api(`/orders/${checkOrderId}`, { token: userToken });
|
||||
check(
|
||||
'رد چک به اصلاح میرود نه بنبست',
|
||||
afterCheckReject.data?.status === 'check_needs_fix',
|
||||
`status=${afterCheckReject.data?.status}`,
|
||||
);
|
||||
|
||||
const fixChecks = await api(`/orders/${checkOrderId}/checks`, {
|
||||
method: 'PATCH',
|
||||
token: userToken,
|
||||
body: { checkMode: 'full', checks: [checkPayload(expectedTotal, 45)] },
|
||||
});
|
||||
check('اصلاح چکها', fixChecks.status < 300, `status=${fixChecks.status} ${JSON.stringify(fixChecks.data).slice(0, 200)}`);
|
||||
check('بازگشت به بررسی چک', fixChecks.data?.status === 'awaiting_check_review', `status=${fixChecks.data?.status}`);
|
||||
|
||||
const fixedChecks = await api(`/admin/orders/${checkOrderId}/checks`, { token: adminToken });
|
||||
const approveCheck = await api(`/admin/checks/${fixedChecks.data[0].id}/status`, {
|
||||
method: 'PATCH',
|
||||
token: adminToken,
|
||||
body: { status: 'approved' },
|
||||
});
|
||||
check('تأیید چک', approveCheck.status < 300, `status=${approveCheck.status}`);
|
||||
const afterCheckApprove = await api(`/orders/${checkOrderId}`, { token: userToken });
|
||||
check('وضعیت تأیید چکها', afterCheckApprove.data?.status === 'checks_approved', `status=${afterCheckApprove.data?.status}`);
|
||||
|
||||
console.log('\n[12] تنظیمات سیستم');
|
||||
const settings = await api('/admin/settings', { token: adminToken });
|
||||
check('خواندن تنظیمات', settings.status < 300 && typeof settings.data?.maxMeteragePerInvoice === 'number', JSON.stringify(settings.data)?.slice(0, 160));
|
||||
const savedSettings = await api('/admin/settings', {
|
||||
method: 'PATCH',
|
||||
token: adminToken,
|
||||
body: { smsPricingReady: false, maxMeteragePerInvoice: 6000 },
|
||||
});
|
||||
check('ذخیره تنظیمات', savedSettings.data?.maxMeteragePerInvoice === 6000 && savedSettings.data?.smsPricingReady === false, JSON.stringify(savedSettings.data)?.slice(0, 200));
|
||||
const companyAfter = await api('/companies/public');
|
||||
check(
|
||||
'سقف متراژ شرکت از تنظیمات ارث میبرد',
|
||||
companyAfter.data?.[0]?.maxMeteragePerInvoice === 6000,
|
||||
`max=${companyAfter.data?.[0]?.maxMeteragePerInvoice}`,
|
||||
);
|
||||
await api('/admin/settings', {
|
||||
method: 'PATCH',
|
||||
token: adminToken,
|
||||
body: { smsPricingReady: true, maxMeteragePerInvoice: 5000 },
|
||||
});
|
||||
|
||||
console.log('\n[13] دسترسی متقاطع بین مشتریها');
|
||||
const otherPhone = '09129999001';
|
||||
await api('/auth/otp/request', {
|
||||
method: 'POST',
|
||||
@@ -285,31 +484,27 @@ async function main() {
|
||||
if (otherToken) {
|
||||
const foreign = await api(`/orders/${cashOrderId}`, { token: otherToken });
|
||||
check('سفارش دیگری قابل خواندن نیست', foreign.status === 403, `status=${foreign.status}`);
|
||||
const foreignReceipt = await api(`/orders/${cashOrderId}/cash-receipt`, {
|
||||
const foreignSettlement = await api(`/orders/${checkOrderId}/settlement`, {
|
||||
method: 'POST',
|
||||
token: otherToken,
|
||||
body: {
|
||||
amount: 1000,
|
||||
transferDate: jalaliIsoInDays(0),
|
||||
transferTime: '12:00',
|
||||
trackingNumber: '1',
|
||||
destinationAccountId: account.id,
|
||||
},
|
||||
body: { settlementType: 'cash' },
|
||||
});
|
||||
check('رسید روی سفارش دیگری رد میشود', foreignReceipt.status === 403, `status=${foreignReceipt.status}`);
|
||||
check('انتخاب تسویه روی سفارش دیگری رد میشود', foreignSettlement.status === 403, `status=${foreignSettlement.status}`);
|
||||
const foreignNotifs = await api('/notifications', { token: otherToken });
|
||||
check('اعلانها بین مشتریها جدا است', (foreignNotifs.data ?? []).length === 0, `count=${(foreignNotifs.data ?? []).length}`);
|
||||
}
|
||||
|
||||
console.log('\n[12] محافظت از ویرایش چک بعد از تأیید');
|
||||
const approve = await api(`/admin/orders/${checkOrderId}/status`, {
|
||||
console.log('\n[14] محافظت از ویرایش بعد از تحویل');
|
||||
const forceDeliver = await api(`/admin/orders/${checkOrderId}/status`, {
|
||||
method: 'PATCH',
|
||||
token: adminToken,
|
||||
body: { status: 'delivered' },
|
||||
body: { status: 'delivered', force: true, reason: 'تحویل دستی برای تست' },
|
||||
});
|
||||
check('تغییر وضعیت توسط ادمین', approve.status < 300, `status=${approve.status}`);
|
||||
check('گذر دستی با دلیل پذیرفته میشود', forceDeliver.status < 300, `status=${forceDeliver.status} ${JSON.stringify(forceDeliver.data).slice(0, 150)}`);
|
||||
const lateEdit = await api(`/orders/${checkOrderId}/checks`, {
|
||||
method: 'PATCH',
|
||||
token: userToken,
|
||||
body: { checks: [{ amount: 1, dueDate: jalaliIsoInDays(10), mode: 'brief' }] },
|
||||
body: { checks: [checkPayload(1000, 10)] },
|
||||
});
|
||||
check('ویرایش چک بعد از تحویل رد میشود', lateEdit.status === 400, `status=${lateEdit.status}`);
|
||||
const lateReceipt = await api(`/orders/${checkOrderId}/cash-receipt`, {
|
||||
@@ -317,7 +512,7 @@ async function main() {
|
||||
token: userToken,
|
||||
body: {
|
||||
amount: 1000,
|
||||
transferDate: jalaliIsoInDays(0),
|
||||
transferDate: isoInDays(0),
|
||||
transferTime: '12:00',
|
||||
trackingNumber: '1',
|
||||
destinationAccountId: account.id,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
import { PrismaModule } from './prisma/prisma.module';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { JwtAuthGuard } from './common/guards/jwt-auth.guard';
|
||||
@@ -16,10 +17,12 @@ import { ShipmentsModule } from './shipments/shipments.module';
|
||||
import { DashboardModule } from './dashboard/dashboard.module';
|
||||
import { SettingsModule } from './settings/settings.module';
|
||||
import { CartsModule } from './carts/carts.module';
|
||||
import { NotificationsModule } from './notifications/notifications.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
ScheduleModule.forRoot(),
|
||||
PrismaModule,
|
||||
AuthModule,
|
||||
CompaniesModule,
|
||||
@@ -34,6 +37,7 @@ import { CartsModule } from './carts/carts.module';
|
||||
DashboardModule,
|
||||
SettingsModule,
|
||||
CartsModule,
|
||||
NotificationsModule,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
|
||||
@@ -47,7 +47,6 @@ export class CartsService {
|
||||
data: {
|
||||
meterage: newMeterage,
|
||||
notes: dto.notes?.trim() || existing.notes,
|
||||
unitPrice: product.basePrice,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
@@ -60,7 +59,6 @@ export class CartsService {
|
||||
productCode: product.code,
|
||||
color: product.color,
|
||||
meterage: dto.meterage,
|
||||
unitPrice: product.basePrice,
|
||||
notes: dto.notes?.trim() || null,
|
||||
companyId: product.companyId,
|
||||
companyName: product.company.name,
|
||||
@@ -188,7 +186,6 @@ export class CartsService {
|
||||
productCode: string;
|
||||
color: string;
|
||||
meterage: number;
|
||||
unitPrice: number | null;
|
||||
notes: string | null;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
@@ -209,7 +206,6 @@ export class CartsService {
|
||||
productCode: item.productCode,
|
||||
color: item.color,
|
||||
meterage: item.meterage,
|
||||
unitPrice: item.unitPrice ?? undefined,
|
||||
notes: item.notes ?? undefined,
|
||||
companyId: item.companyId,
|
||||
companyName: item.companyName,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { CashPaymentsController } from './cash-payments.controller';
|
||||
import { CashPaymentsService } from './cash-payments.service';
|
||||
import { NotificationsModule } from '../notifications/notifications.module';
|
||||
|
||||
@Module({
|
||||
imports: [NotificationsModule],
|
||||
controllers: [CashPaymentsController],
|
||||
providers: [CashPaymentsService],
|
||||
exports: [CashPaymentsService],
|
||||
|
||||
@@ -11,14 +11,10 @@ import {
|
||||
UpdateCashPaymentStatusDto,
|
||||
} from './dto/cash-payment.dto';
|
||||
import { joinFullName } from '../common/utils/name';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
|
||||
/** بعد از تأیید پرداخت یا ارسال، رسید جدید نباید وضعیت را به عقب برگرداند. */
|
||||
const RECEIPT_SUBMITTABLE_STATUSES: OrderStatus[] = [
|
||||
OrderStatus.awaiting_review,
|
||||
OrderStatus.pricing,
|
||||
OrderStatus.needs_correction,
|
||||
OrderStatus.awaiting_settlement,
|
||||
OrderStatus.awaiting_settlement_fix,
|
||||
OrderStatus.cash_in_progress,
|
||||
OrderStatus.cash_invoice_issued,
|
||||
OrderStatus.awaiting_cash_payment,
|
||||
@@ -28,7 +24,10 @@ const RECEIPT_SUBMITTABLE_STATUSES: OrderStatus[] = [
|
||||
|
||||
@Injectable()
|
||||
export class CashPaymentsService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly notifications: NotificationsService,
|
||||
) {}
|
||||
|
||||
async submitReceipt(
|
||||
userId: string,
|
||||
@@ -112,6 +111,14 @@ export class CashPaymentsService {
|
||||
return saved;
|
||||
});
|
||||
|
||||
this.notifications.dispatchAdmin({
|
||||
title: 'رسید پرداخت نقدی ثبت شد',
|
||||
body: `مشتری برای سفارش ${order.orderNumber} رسید به مبلغ ${Math.round(
|
||||
dto.amount,
|
||||
).toLocaleString('fa-IR')} تومان ثبت کرد.`,
|
||||
link: `/orders/${orderId}`,
|
||||
});
|
||||
|
||||
return this.mapReceipt(receipt, order.orderNumber, '');
|
||||
}
|
||||
|
||||
@@ -157,11 +164,12 @@ export class CashPaymentsService {
|
||||
include: { order: { include: { user: true } } },
|
||||
});
|
||||
|
||||
// رد رسید باید مشتری را به مرحله پرداخت برگرداند تا بتواند رسید تازه بفرستد.
|
||||
let nextStatus: OrderStatus | null = null;
|
||||
if (dto.status === CashPaymentStatus.approved) {
|
||||
nextStatus = OrderStatus.cash_confirmed;
|
||||
} else if (dto.status === CashPaymentStatus.rejected) {
|
||||
nextStatus = OrderStatus.needs_correction;
|
||||
nextStatus = OrderStatus.awaiting_cash_payment;
|
||||
} else if (dto.status === CashPaymentStatus.expired) {
|
||||
nextStatus = OrderStatus.expired;
|
||||
}
|
||||
@@ -185,6 +193,24 @@ export class CashPaymentsService {
|
||||
return result;
|
||||
});
|
||||
|
||||
if (dto.status === CashPaymentStatus.approved) {
|
||||
this.notifications.dispatchOrderEvent(
|
||||
receipt.orderId,
|
||||
'receipt_approved',
|
||||
);
|
||||
} else if (dto.status === CashPaymentStatus.rejected) {
|
||||
this.notifications.dispatchOrderEvent(
|
||||
receipt.orderId,
|
||||
'receipt_rejected',
|
||||
{ note: dto.note ?? null },
|
||||
);
|
||||
} else if (dto.status === CashPaymentStatus.expired) {
|
||||
this.notifications.dispatchOrderEvent(
|
||||
receipt.orderId,
|
||||
'cash_invoice_expired',
|
||||
);
|
||||
}
|
||||
|
||||
return this.mapReceipt(
|
||||
updated,
|
||||
updated.order.orderNumber,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ChecksController } from './checks.controller';
|
||||
import { ChecksService } from './checks.service';
|
||||
import { NotificationsModule } from '../notifications/notifications.module';
|
||||
|
||||
@Module({
|
||||
imports: [NotificationsModule],
|
||||
controllers: [ChecksController],
|
||||
providers: [ChecksService],
|
||||
exports: [ChecksService],
|
||||
|
||||
@@ -2,10 +2,14 @@ import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { CheckStatus, OrderStatus } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { UpdateCheckStatusDto } from './dto/check.dto';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
|
||||
@Injectable()
|
||||
export class ChecksService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly notifications: NotificationsService,
|
||||
) {}
|
||||
|
||||
async findByOrder(orderId: string) {
|
||||
const order = await this.prisma.order.findUnique({
|
||||
@@ -27,6 +31,7 @@ export class ChecksService {
|
||||
});
|
||||
if (!check) throw new NotFoundException('چک یافت نشد');
|
||||
|
||||
let notifyStatus: OrderStatus | null = null;
|
||||
const updated = await this.prisma.$transaction(async (tx) => {
|
||||
const result = await tx.check.update({
|
||||
where: { id },
|
||||
@@ -48,13 +53,12 @@ export class ChecksService {
|
||||
(c) => c.status === CheckStatus.rejected,
|
||||
);
|
||||
|
||||
// رد چک باید قابل اصلاح بماند؛ check_rejected راه برگشتی برای مشتری ندارد.
|
||||
let nextStatus: OrderStatus | null = null;
|
||||
if (anyRejected) {
|
||||
nextStatus = OrderStatus.check_rejected;
|
||||
nextStatus = OrderStatus.check_needs_fix;
|
||||
} else if (allApproved) {
|
||||
nextStatus = OrderStatus.checks_approved;
|
||||
} else if (dto.status === CheckStatus.rejected) {
|
||||
nextStatus = OrderStatus.check_needs_fix;
|
||||
}
|
||||
|
||||
if (nextStatus && nextStatus !== check.order.status) {
|
||||
@@ -71,11 +75,20 @@ export class ChecksService {
|
||||
note: dto.note || `وضعیت چک به ${dto.status} تغییر کرد`,
|
||||
},
|
||||
});
|
||||
notifyStatus = nextStatus;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
if (notifyStatus === OrderStatus.checks_approved) {
|
||||
this.notifications.dispatchOrderEvent(check.orderId, 'checks_approved');
|
||||
} else if (notifyStatus === OrderStatus.check_needs_fix) {
|
||||
this.notifications.dispatchOrderEvent(check.orderId, 'checks_need_fix', {
|
||||
note: dto.note ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
return this.map(updated);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/** وقتی نه شرکت و نه تنظیمات سیستم مقداری ندارند. */
|
||||
export const FALLBACK_MAX_METERAGE = 5000;
|
||||
|
||||
/**
|
||||
* سقف متراژ موثر هر فاکتور: مقدار شرکت، در نبودش تنظیمات سیستم.
|
||||
*/
|
||||
export function resolveMaxMeterage(
|
||||
companyValue: number | null | undefined,
|
||||
settingsValue: number | null | undefined,
|
||||
): number {
|
||||
if (companyValue != null && companyValue > 0) return companyValue;
|
||||
if (settingsValue != null && settingsValue > 0) return settingsValue;
|
||||
return FALLBACK_MAX_METERAGE;
|
||||
}
|
||||
@@ -4,22 +4,24 @@ import { OrderStatus } from '@prisma/client';
|
||||
export const ADMIN_STATUS_MAP: Record<string, OrderStatus[]> = {
|
||||
pending_review: [
|
||||
OrderStatus.cart,
|
||||
OrderStatus.awaiting_settlement,
|
||||
OrderStatus.cash_in_progress,
|
||||
OrderStatus.check_in_progress,
|
||||
OrderStatus.submitted,
|
||||
OrderStatus.awaiting_review,
|
||||
OrderStatus.pricing,
|
||||
],
|
||||
awaiting_customer: [
|
||||
OrderStatus.awaiting_settlement,
|
||||
OrderStatus.awaiting_settlement_fix,
|
||||
OrderStatus.check_in_progress,
|
||||
OrderStatus.cash_in_progress,
|
||||
OrderStatus.cash_invoice_issued,
|
||||
OrderStatus.awaiting_cash_payment,
|
||||
],
|
||||
pending_check: [OrderStatus.awaiting_check_review],
|
||||
pending_payment: [
|
||||
OrderStatus.cash_payment_submitted,
|
||||
OrderStatus.awaiting_cash_confirmation,
|
||||
],
|
||||
pending_check: [OrderStatus.awaiting_check_review],
|
||||
needs_correction: [
|
||||
OrderStatus.needs_correction,
|
||||
OrderStatus.awaiting_settlement_fix,
|
||||
],
|
||||
needs_correction: [OrderStatus.needs_correction],
|
||||
check_correction: [OrderStatus.check_needs_fix, OrderStatus.check_rejected],
|
||||
final_approval: [
|
||||
OrderStatus.final_approved,
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import { OrderStatus } from '@prisma/client';
|
||||
import { ORDER_STATUS_LABELS } from './order-status';
|
||||
|
||||
/**
|
||||
* گذرهای مجاز گردش سفارش. enum دیتابیس وضعیتهای بیشتری دارد که از نسخههای
|
||||
* قبلی ماندهاند؛ فقط همینها در جریان واقعی استفاده میشوند.
|
||||
*
|
||||
* ترتیب کار: ثبت سفارش ← قیمتگذاری ادمین ← ارسال فاکتور ← انتخاب تسویه توسط
|
||||
* مشتری ← شاخه نقدی یا چکی ← تأیید نهایی ← ارسال.
|
||||
*/
|
||||
export const ORDER_TRANSITIONS: Record<OrderStatus, OrderStatus[]> = {
|
||||
awaiting_review: [OrderStatus.pricing],
|
||||
pricing: [OrderStatus.awaiting_settlement],
|
||||
awaiting_settlement: [
|
||||
OrderStatus.awaiting_cash_payment,
|
||||
OrderStatus.check_in_progress,
|
||||
OrderStatus.pricing,
|
||||
],
|
||||
|
||||
// شاخه نقدی
|
||||
awaiting_cash_payment: [
|
||||
OrderStatus.awaiting_cash_confirmation,
|
||||
OrderStatus.expired,
|
||||
],
|
||||
awaiting_cash_confirmation: [
|
||||
OrderStatus.cash_confirmed,
|
||||
OrderStatus.awaiting_cash_payment,
|
||||
],
|
||||
cash_confirmed: [OrderStatus.final_approved],
|
||||
|
||||
// شاخه چکی
|
||||
check_in_progress: [OrderStatus.awaiting_check_review],
|
||||
awaiting_check_review: [
|
||||
OrderStatus.checks_approved,
|
||||
OrderStatus.check_needs_fix,
|
||||
],
|
||||
check_needs_fix: [OrderStatus.awaiting_check_review],
|
||||
checks_approved: [OrderStatus.final_approved],
|
||||
|
||||
// مسیر مشترک تا تحویل
|
||||
final_approved: [OrderStatus.preparing],
|
||||
preparing: [OrderStatus.ready_to_ship],
|
||||
ready_to_ship: [OrderStatus.shipped],
|
||||
shipped: [OrderStatus.delivered],
|
||||
delivered: [],
|
||||
|
||||
// پایانیها
|
||||
cancelled: [],
|
||||
rejected: [],
|
||||
expired: [],
|
||||
|
||||
// وضعیتهای قدیمی: فقط راه خروج دارند تا رکوردهای موجود قفل نشوند
|
||||
cart: [OrderStatus.awaiting_review],
|
||||
submitted: [OrderStatus.awaiting_review],
|
||||
needs_correction: [OrderStatus.awaiting_review, OrderStatus.pricing],
|
||||
awaiting_settlement_fix: [OrderStatus.awaiting_settlement],
|
||||
cash_in_progress: [OrderStatus.awaiting_cash_payment],
|
||||
cash_invoice_issued: [OrderStatus.awaiting_cash_payment],
|
||||
cash_payment_submitted: [OrderStatus.awaiting_cash_confirmation],
|
||||
check_rejected: [OrderStatus.check_needs_fix],
|
||||
};
|
||||
|
||||
/** ادمین در هر مرحلهای میتواند سفارش را ببندد. */
|
||||
const ADMIN_TERMINAL_TRANSITIONS: OrderStatus[] = [
|
||||
OrderStatus.cancelled,
|
||||
OrderStatus.rejected,
|
||||
];
|
||||
|
||||
const FINAL_STATUSES: OrderStatus[] = [
|
||||
OrderStatus.delivered,
|
||||
OrderStatus.cancelled,
|
||||
OrderStatus.rejected,
|
||||
OrderStatus.expired,
|
||||
];
|
||||
|
||||
export function isFinalStatus(status: OrderStatus): boolean {
|
||||
return FINAL_STATUSES.includes(status);
|
||||
}
|
||||
|
||||
/** گذرهایی که ادمین بدون حالت اجباری میتواند انتخاب کند. */
|
||||
export function allowedTransitions(from: OrderStatus): OrderStatus[] {
|
||||
const next = [...(ORDER_TRANSITIONS[from] ?? [])];
|
||||
if (!isFinalStatus(from)) {
|
||||
for (const terminal of ADMIN_TERMINAL_TRANSITIONS) {
|
||||
if (!next.includes(terminal)) next.push(terminal);
|
||||
}
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
export function canTransition(from: OrderStatus, to: OrderStatus): boolean {
|
||||
return allowedTransitions(from).includes(to);
|
||||
}
|
||||
|
||||
export function assertTransition(from: OrderStatus, to: OrderStatus): void {
|
||||
if (canTransition(from, to)) return;
|
||||
throw new BadRequestException(
|
||||
`تغییر وضعیت از «${ORDER_STATUS_LABELS[from]}» به «${ORDER_STATUS_LABELS[to]}» مجاز نیست`,
|
||||
);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { resolveMaxMeterage } from '../common/utils/meterage';
|
||||
import {
|
||||
CreateBankAccountDto,
|
||||
CreateCompanyDto,
|
||||
@@ -17,35 +18,50 @@ export class CompaniesService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async findAll(activeOnly = false) {
|
||||
const companies = await this.prisma.company.findMany({
|
||||
where: activeOnly ? { isActive: true } : undefined,
|
||||
orderBy: [{ displayOrder: 'asc' }, { name: 'asc' }],
|
||||
include: {
|
||||
_count: {
|
||||
select: { catalogs: true, products: true, bankAccounts: true },
|
||||
const [companies, fallbackMeterage] = await Promise.all([
|
||||
this.prisma.company.findMany({
|
||||
where: activeOnly ? { isActive: true } : undefined,
|
||||
orderBy: [{ displayOrder: 'asc' }, { name: 'asc' }],
|
||||
include: {
|
||||
_count: {
|
||||
select: { catalogs: true, products: true, bankAccounts: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
return companies.map((c) => this.mapCompany(c));
|
||||
}),
|
||||
this.settingsMaxMeterage(),
|
||||
]);
|
||||
return companies.map((c) => this.mapCompany(c, fallbackMeterage));
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
const company = await this.prisma.company.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
bankAccounts: { orderBy: { displayOrder: 'asc' } },
|
||||
_count: {
|
||||
select: { catalogs: true, products: true, bankAccounts: true },
|
||||
const [company, fallbackMeterage] = await Promise.all([
|
||||
this.prisma.company.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
bankAccounts: { orderBy: { displayOrder: 'asc' } },
|
||||
_count: {
|
||||
select: { catalogs: true, products: true, bankAccounts: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
this.settingsMaxMeterage(),
|
||||
]);
|
||||
if (!company) throw new NotFoundException('شرکت یافت نشد');
|
||||
return {
|
||||
...this.mapCompany(company),
|
||||
...this.mapCompany(company, fallbackMeterage),
|
||||
bankAccounts: company.bankAccounts.map((b) => this.mapBankAccount(b)),
|
||||
};
|
||||
}
|
||||
|
||||
/** سقف متراژ تنظیمات سیستم که شرکتهای بدون سقف اختصاصی از آن ارث میبرند. */
|
||||
private async settingsMaxMeterage(): Promise<number | null> {
|
||||
const settings = await this.prisma.systemSettings.findUnique({
|
||||
where: { id: 'default' },
|
||||
select: { maxMeteragePerInvoice: true },
|
||||
});
|
||||
return settings?.maxMeteragePerInvoice ?? null;
|
||||
}
|
||||
|
||||
async create(dto: CreateCompanyDto) {
|
||||
try {
|
||||
const company = await this.prisma.company.create({
|
||||
@@ -55,7 +71,7 @@ export class CompaniesService {
|
||||
description: dto.description?.trim() || null,
|
||||
logoUrl: dto.logoUrl?.trim() || null,
|
||||
displayOrder: dto.displayOrder ?? 0,
|
||||
maxMeteragePerInvoice: dto.maxMeteragePerInvoice ?? 5000,
|
||||
maxMeteragePerInvoice: dto.maxMeteragePerInvoice ?? null,
|
||||
cashInvoiceValidityHours: dto.cashInvoiceValidityHours ?? 8,
|
||||
cashInvoiceValidUntilHour: dto.cashInvoiceValidUntilHour ?? null,
|
||||
isActive: dto.isActive ?? true,
|
||||
@@ -196,21 +212,24 @@ export class CompaniesService {
|
||||
return account;
|
||||
}
|
||||
|
||||
private mapCompany(company: {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
logoUrl: string | null;
|
||||
isActive: boolean;
|
||||
displayOrder: number;
|
||||
maxMeteragePerInvoice: number;
|
||||
cashInvoiceValidityHours: number;
|
||||
cashInvoiceValidUntilHour: number | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
_count?: { catalogs: number; products: number; bankAccounts: number };
|
||||
}) {
|
||||
private mapCompany(
|
||||
company: {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
logoUrl: string | null;
|
||||
isActive: boolean;
|
||||
displayOrder: number;
|
||||
maxMeteragePerInvoice: number | null;
|
||||
cashInvoiceValidityHours: number;
|
||||
cashInvoiceValidUntilHour: number | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
_count?: { catalogs: number; products: number; bankAccounts: number };
|
||||
},
|
||||
fallbackMaxMeterage: number | null = null,
|
||||
) {
|
||||
return {
|
||||
id: company.id,
|
||||
name: company.name,
|
||||
@@ -220,7 +239,12 @@ export class CompaniesService {
|
||||
logo: company.logoUrl ?? undefined,
|
||||
isActive: company.isActive,
|
||||
displayOrder: company.displayOrder,
|
||||
maxMeteragePerInvoice: company.maxMeteragePerInvoice,
|
||||
maxMeteragePerInvoice: resolveMaxMeterage(
|
||||
company.maxMeteragePerInvoice,
|
||||
fallbackMaxMeterage,
|
||||
),
|
||||
/** مقدار خام شرکت؛ خالی یعنی از تنظیمات سیستم ارث میبرد */
|
||||
ownMaxMeteragePerInvoice: company.maxMeteragePerInvoice ?? undefined,
|
||||
cashInvoiceValidityHours: company.cashInvoiceValidityHours,
|
||||
cashInvoiceValidUntilHour: company.cashInvoiceValidUntilHour ?? undefined,
|
||||
catalogCount: company._count?.catalogs ?? 0,
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
import { NotificationTone, OrderStatus } from '@prisma/client';
|
||||
|
||||
/** رویدادهایی که برای مشتری اعلان و پیامک تولید میکنند. */
|
||||
export type CustomerEvent =
|
||||
| 'order_submitted'
|
||||
| 'invoice_priced'
|
||||
| 'checks_approved'
|
||||
| 'checks_need_fix'
|
||||
| 'receipt_approved'
|
||||
| 'receipt_rejected'
|
||||
| 'cash_invoice_reminder'
|
||||
| 'cash_invoice_expired'
|
||||
| 'order_preparing'
|
||||
| 'order_ready_to_ship'
|
||||
| 'order_shipped'
|
||||
| 'order_delivered'
|
||||
| 'order_cancelled';
|
||||
|
||||
/** کلید سوئیچهای پیامک در SystemSettings؛ null یعنی پیامک ندارد. */
|
||||
type SmsSwitch =
|
||||
| 'smsOrderRegistered'
|
||||
| 'smsPricingReady'
|
||||
| 'smsPaymentReminder'
|
||||
| 'smsCheckReview'
|
||||
| 'smsShippingUpdate'
|
||||
| null;
|
||||
|
||||
export type CustomerEventTemplate = {
|
||||
title: string;
|
||||
/** متن اعلان درونبرنامهای */
|
||||
body: (ctx: EventContext) => string;
|
||||
/** متن پیامک؛ نبودنش یعنی فقط اعلان درونبرنامهای */
|
||||
sms?: (ctx: EventContext) => string;
|
||||
tone: NotificationTone;
|
||||
smsSwitch: SmsSwitch;
|
||||
};
|
||||
|
||||
export type EventContext = {
|
||||
orderNumber: string;
|
||||
appName: string;
|
||||
amount?: number | null;
|
||||
note?: string | null;
|
||||
deadline?: string | null;
|
||||
carrier?: string | null;
|
||||
trackingNumber?: string | null;
|
||||
};
|
||||
|
||||
function money(amount?: number | null): string {
|
||||
if (!amount) return '—';
|
||||
return `${Math.round(amount).toLocaleString('fa-IR')} تومان`;
|
||||
}
|
||||
|
||||
/**
|
||||
* وضعیتهایی که بهتنهایی برای مشتری خبر دارند. وضعیتهای میانی مثل
|
||||
* awaiting_check_review خبری برای مشتری ندارند و اینجا نمیآیند.
|
||||
*/
|
||||
export function eventForStatus(status: OrderStatus): CustomerEvent | null {
|
||||
switch (status) {
|
||||
case OrderStatus.awaiting_settlement:
|
||||
return 'invoice_priced';
|
||||
case OrderStatus.preparing:
|
||||
case OrderStatus.final_approved:
|
||||
return 'order_preparing';
|
||||
case OrderStatus.ready_to_ship:
|
||||
return 'order_ready_to_ship';
|
||||
case OrderStatus.shipped:
|
||||
return 'order_shipped';
|
||||
case OrderStatus.delivered:
|
||||
return 'order_delivered';
|
||||
case OrderStatus.cancelled:
|
||||
case OrderStatus.rejected:
|
||||
return 'order_cancelled';
|
||||
case OrderStatus.expired:
|
||||
return 'cash_invoice_expired';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export const CUSTOMER_EVENTS: Record<CustomerEvent, CustomerEventTemplate> = {
|
||||
order_submitted: {
|
||||
title: 'سفارش شما ثبت شد',
|
||||
body: (c) =>
|
||||
`سفارش ${c.orderNumber} ثبت شد و برای بررسی و قیمتگذاری به کارشناسان ما رسید.`,
|
||||
sms: (c) =>
|
||||
`سفارش ${c.orderNumber} در ${c.appName} ثبت شد. پس از قیمتگذاری، فاکتور برای شما ارسال میشود.`,
|
||||
tone: NotificationTone.info,
|
||||
smsSwitch: 'smsOrderRegistered',
|
||||
},
|
||||
invoice_priced: {
|
||||
title: 'فاکتور سفارش شما صادر شد',
|
||||
body: (c) =>
|
||||
`مبلغ فاکتور سفارش ${c.orderNumber} ${money(c.amount)} است. برای ادامه، روش تسویه را انتخاب کنید.`,
|
||||
sms: (c) =>
|
||||
`فاکتور سفارش ${c.orderNumber} به مبلغ ${money(c.amount)} صادر شد. برای انتخاب روش تسویه به پنل ${c.appName} مراجعه کنید.`,
|
||||
tone: NotificationTone.success,
|
||||
smsSwitch: 'smsPricingReady',
|
||||
},
|
||||
checks_approved: {
|
||||
title: 'چکهای شما تایید شد',
|
||||
body: (c) => `همه چکهای سفارش ${c.orderNumber} تایید شدند.`,
|
||||
sms: (c) =>
|
||||
`چکهای سفارش ${c.orderNumber} تایید شد و سفارش به مرحله آمادهسازی میرود.`,
|
||||
tone: NotificationTone.success,
|
||||
smsSwitch: 'smsCheckReview',
|
||||
},
|
||||
checks_need_fix: {
|
||||
title: 'چک شما نیاز به اصلاح دارد',
|
||||
body: (c) =>
|
||||
`یک یا چند چک سفارش ${c.orderNumber} تایید نشد${c.note ? `: ${c.note}` : '.'} لطفاً چکها را اصلاح و مجدداً ارسال کنید.`,
|
||||
sms: (c) =>
|
||||
`چک سفارش ${c.orderNumber} تایید نشد. برای اصلاح چکها به پنل ${c.appName} مراجعه کنید.`,
|
||||
tone: NotificationTone.danger,
|
||||
smsSwitch: 'smsCheckReview',
|
||||
},
|
||||
receipt_approved: {
|
||||
title: 'پرداخت شما تایید شد',
|
||||
body: (c) => `رسید پرداخت سفارش ${c.orderNumber} تایید شد.`,
|
||||
sms: (c) =>
|
||||
`رسید پرداخت سفارش ${c.orderNumber} تایید شد. سفارش شما در حال آمادهسازی است.`,
|
||||
tone: NotificationTone.success,
|
||||
smsSwitch: 'smsPaymentReminder',
|
||||
},
|
||||
receipt_rejected: {
|
||||
title: 'رسید پرداخت تایید نشد',
|
||||
body: (c) =>
|
||||
`رسید پرداخت سفارش ${c.orderNumber} تایید نشد${c.note ? `: ${c.note}` : '.'} لطفاً رسید صحیح را ثبت کنید.`,
|
||||
sms: (c) =>
|
||||
`رسید پرداخت سفارش ${c.orderNumber} تایید نشد. لطفاً در پنل ${c.appName} رسید را اصلاح کنید.`,
|
||||
tone: NotificationTone.danger,
|
||||
smsSwitch: 'smsPaymentReminder',
|
||||
},
|
||||
cash_invoice_reminder: {
|
||||
title: 'یادآوری مهلت پرداخت',
|
||||
body: (c) =>
|
||||
`مهلت پرداخت فاکتور سفارش ${c.orderNumber} به پایان نزدیک است. مبلغ قابل پرداخت ${money(c.amount)}.`,
|
||||
sms: (c) =>
|
||||
`مهلت پرداخت فاکتور سفارش ${c.orderNumber} در حال پایان است. مبلغ ${money(c.amount)}.`,
|
||||
tone: NotificationTone.warning,
|
||||
smsSwitch: 'smsPaymentReminder',
|
||||
},
|
||||
cash_invoice_expired: {
|
||||
title: 'مهلت پرداخت فاکتور تمام شد',
|
||||
body: (c) =>
|
||||
`مهلت پرداخت فاکتور سفارش ${c.orderNumber} به پایان رسید. برای ثبت سفارش تازه با پشتیبانی تماس بگیرید.`,
|
||||
sms: (c) => `مهلت پرداخت فاکتور سفارش ${c.orderNumber} به پایان رسید.`,
|
||||
tone: NotificationTone.danger,
|
||||
smsSwitch: 'smsPaymentReminder',
|
||||
},
|
||||
order_preparing: {
|
||||
title: 'سفارش در حال آمادهسازی',
|
||||
body: (c) =>
|
||||
`سفارش ${c.orderNumber} تایید نهایی شد و در حال آمادهسازی است.`,
|
||||
tone: NotificationTone.info,
|
||||
smsSwitch: null,
|
||||
},
|
||||
order_ready_to_ship: {
|
||||
title: 'سفارش آماده ارسال است',
|
||||
body: (c) => `سفارش ${c.orderNumber} آماده ارسال شد.`,
|
||||
sms: (c) => `سفارش ${c.orderNumber} آماده ارسال شد.`,
|
||||
tone: NotificationTone.info,
|
||||
smsSwitch: 'smsShippingUpdate',
|
||||
},
|
||||
order_shipped: {
|
||||
title: 'سفارش ارسال شد',
|
||||
body: (c) =>
|
||||
`سفارش ${c.orderNumber} ارسال شد${c.carrier ? ` (${c.carrier})` : ''}${
|
||||
c.trackingNumber ? ` با کد رهگیری ${c.trackingNumber}` : ''
|
||||
}.`,
|
||||
sms: (c) =>
|
||||
`سفارش ${c.orderNumber} ارسال شد${
|
||||
c.trackingNumber ? `. کد رهگیری: ${c.trackingNumber}` : '.'
|
||||
}`,
|
||||
tone: NotificationTone.success,
|
||||
smsSwitch: 'smsShippingUpdate',
|
||||
},
|
||||
order_delivered: {
|
||||
title: 'سفارش تحویل شد',
|
||||
body: (c) =>
|
||||
`سفارش ${c.orderNumber} تحویل داده شد. از خرید شما سپاسگزاریم.`,
|
||||
sms: (c) => `سفارش ${c.orderNumber} تحویل داده شد. از خرید شما سپاسگزاریم.`,
|
||||
tone: NotificationTone.success,
|
||||
smsSwitch: 'smsShippingUpdate',
|
||||
},
|
||||
order_cancelled: {
|
||||
title: 'سفارش لغو شد',
|
||||
body: (c) =>
|
||||
`سفارش ${c.orderNumber} لغو شد${c.note ? `: ${c.note}` : '.'} برای اطلاعات بیشتر با پشتیبانی تماس بگیرید.`,
|
||||
sms: (c) => `سفارش ${c.orderNumber} لغو شد.`,
|
||||
tone: NotificationTone.danger,
|
||||
smsSwitch: null,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Controller, Get, Param, Patch } from '@nestjs/common';
|
||||
import { NotificationsService } from './notifications.service';
|
||||
import { AuthType } from '../common/decorators/auth.decorators';
|
||||
import { CurrentUser } from '../common/decorators/current-user.decorator';
|
||||
import type { AuthPrincipal } from '../common/decorators/current-user.decorator';
|
||||
|
||||
@Controller('notifications')
|
||||
@AuthType('customer')
|
||||
export class CustomerNotificationsController {
|
||||
constructor(private readonly notifications: NotificationsService) {}
|
||||
|
||||
@Get()
|
||||
list(@CurrentUser() user: AuthPrincipal) {
|
||||
return this.notifications.listForCustomer(user.id);
|
||||
}
|
||||
|
||||
@Get('unread-count')
|
||||
unreadCount(@CurrentUser() user: AuthPrincipal) {
|
||||
return this.notifications.unreadCount(user.id);
|
||||
}
|
||||
|
||||
@Patch('read-all')
|
||||
markAllRead(@CurrentUser() user: AuthPrincipal) {
|
||||
return this.notifications.markAllRead(user.id);
|
||||
}
|
||||
|
||||
@Patch(':id/read')
|
||||
markRead(@CurrentUser() user: AuthPrincipal, @Param('id') id: string) {
|
||||
return this.notifications.markRead(user.id, id);
|
||||
}
|
||||
}
|
||||
|
||||
@Controller('admin/notifications')
|
||||
@AuthType('admin')
|
||||
export class AdminNotificationsController {
|
||||
constructor(private readonly notifications: NotificationsService) {}
|
||||
|
||||
@Patch('read-all')
|
||||
markAllRead() {
|
||||
return this.notifications.markAllAdminRead();
|
||||
}
|
||||
|
||||
@Patch(':id/read')
|
||||
markRead(@Param('id') id: string) {
|
||||
return this.notifications.markAdminRead(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { NotificationsService } from './notifications.service';
|
||||
import {
|
||||
AdminNotificationsController,
|
||||
CustomerNotificationsController,
|
||||
} from './notifications.controller';
|
||||
import { SmsModule } from '../sms/sms.module';
|
||||
|
||||
@Module({
|
||||
imports: [SmsModule],
|
||||
controllers: [CustomerNotificationsController, AdminNotificationsController],
|
||||
providers: [NotificationsService],
|
||||
exports: [NotificationsService],
|
||||
})
|
||||
export class NotificationsModule {}
|
||||
@@ -0,0 +1,224 @@
|
||||
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { NotificationTone } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { SmsService } from '../sms/sms.service';
|
||||
import {
|
||||
CUSTOMER_EVENTS,
|
||||
type CustomerEvent,
|
||||
type EventContext,
|
||||
} from './notification-events';
|
||||
|
||||
type NotifyOptions = {
|
||||
amount?: number | null;
|
||||
note?: string | null;
|
||||
carrier?: string | null;
|
||||
trackingNumber?: string | null;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class NotificationsService {
|
||||
private readonly logger = new Logger(NotificationsService.name);
|
||||
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly sms: SmsService,
|
||||
private readonly config: ConfigService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* باید **بعد از commit تراکنش** صدا زده شود؛ وگرنه با rollback هم پیامک رفته
|
||||
* است و هم اعلانی برای سفارشی ساخته میشود که وجود ندارد.
|
||||
*/
|
||||
async notifyOrderEvent(
|
||||
orderId: string,
|
||||
event: CustomerEvent,
|
||||
options: NotifyOptions = {},
|
||||
): Promise<void> {
|
||||
try {
|
||||
const order = await this.prisma.order.findUnique({
|
||||
where: { id: orderId },
|
||||
select: {
|
||||
id: true,
|
||||
orderNumber: true,
|
||||
userId: true,
|
||||
finalAmount: true,
|
||||
totalAmount: true,
|
||||
user: { select: { phone: true } },
|
||||
},
|
||||
});
|
||||
if (!order) return;
|
||||
|
||||
const template = CUSTOMER_EVENTS[event];
|
||||
const ctx: EventContext = {
|
||||
orderNumber: order.orderNumber,
|
||||
appName: this.appName(),
|
||||
amount:
|
||||
options.amount ?? order.finalAmount ?? order.totalAmount ?? undefined,
|
||||
note: options.note ?? null,
|
||||
carrier: options.carrier ?? null,
|
||||
trackingNumber: options.trackingNumber ?? null,
|
||||
};
|
||||
|
||||
const notification = await this.prisma.customerNotification.create({
|
||||
data: {
|
||||
customerId: order.userId,
|
||||
orderId: order.id,
|
||||
event,
|
||||
title: template.title,
|
||||
body: template.body(ctx),
|
||||
tone: template.tone,
|
||||
link: `/orders/${order.id}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!template.sms || !template.smsSwitch) return;
|
||||
const enabled = await this.isSmsEventEnabled(template.smsSwitch);
|
||||
if (!enabled || !this.sms.isEnabled()) return;
|
||||
|
||||
const result = await this.sms.send(order.user.phone, template.sms(ctx));
|
||||
if (result.success) {
|
||||
await this.prisma.customerNotification.update({
|
||||
where: { id: notification.id },
|
||||
data: { smsSent: true },
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
// اعلان هیچوقت نباید عملیات اصلی را بشکند
|
||||
this.logger.error(
|
||||
`notifyOrderEvent(${event}) failed for order ${orderId}`,
|
||||
err instanceof Error ? err.stack : err,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** نسخه بدون await برای مسیرهایی که نباید منتظر پیامک بمانند. */
|
||||
dispatchOrderEvent(
|
||||
orderId: string,
|
||||
event: CustomerEvent,
|
||||
options: NotifyOptions = {},
|
||||
): void {
|
||||
void this.notifyOrderEvent(orderId, event, options);
|
||||
}
|
||||
|
||||
async notifyAdmin(input: {
|
||||
title: string;
|
||||
body: string;
|
||||
tone?: NotificationTone;
|
||||
link?: string;
|
||||
}): Promise<void> {
|
||||
try {
|
||||
await this.prisma.adminNotification.create({
|
||||
data: {
|
||||
title: input.title,
|
||||
body: input.body,
|
||||
tone: input.tone ?? NotificationTone.info,
|
||||
link: input.link ?? null,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
this.logger.error(
|
||||
'notifyAdmin failed',
|
||||
err instanceof Error ? err.stack : err,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
dispatchAdmin(input: {
|
||||
title: string;
|
||||
body: string;
|
||||
tone?: NotificationTone;
|
||||
link?: string;
|
||||
}): void {
|
||||
void this.notifyAdmin(input);
|
||||
}
|
||||
|
||||
async listForCustomer(customerId: string, limit = 50) {
|
||||
const rows = await this.prisma.customerNotification.findMany({
|
||||
where: { customerId },
|
||||
orderBy: { sentAt: 'desc' },
|
||||
take: limit,
|
||||
});
|
||||
return rows.map((n) => ({
|
||||
id: n.id,
|
||||
orderId: n.orderId ?? undefined,
|
||||
event: n.event,
|
||||
title: n.title,
|
||||
body: n.body,
|
||||
tone: n.tone ?? NotificationTone.info,
|
||||
link: n.link ?? undefined,
|
||||
isRead: n.isRead,
|
||||
sentAt: n.sentAt.toISOString(),
|
||||
}));
|
||||
}
|
||||
|
||||
async unreadCount(customerId: string) {
|
||||
const count = await this.prisma.customerNotification.count({
|
||||
where: { customerId, isRead: false },
|
||||
});
|
||||
return { count };
|
||||
}
|
||||
|
||||
async markRead(customerId: string, id: string) {
|
||||
const existing = await this.prisma.customerNotification.findFirst({
|
||||
where: { id, customerId },
|
||||
select: { id: true },
|
||||
});
|
||||
if (!existing) throw new NotFoundException('اعلان یافت نشد');
|
||||
await this.prisma.customerNotification.update({
|
||||
where: { id },
|
||||
data: { isRead: true },
|
||||
});
|
||||
return this.unreadCount(customerId);
|
||||
}
|
||||
|
||||
async markAllRead(customerId: string) {
|
||||
await this.prisma.customerNotification.updateMany({
|
||||
where: { customerId, isRead: false },
|
||||
data: { isRead: true },
|
||||
});
|
||||
return { count: 0 };
|
||||
}
|
||||
|
||||
async markAdminRead(id: string) {
|
||||
await this.prisma.adminNotification.updateMany({
|
||||
where: { id },
|
||||
data: { isRead: true },
|
||||
});
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
async markAllAdminRead() {
|
||||
await this.prisma.adminNotification.updateMany({
|
||||
where: { isRead: false },
|
||||
data: { isRead: true },
|
||||
});
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
private appName(): string {
|
||||
return this.config.get<string>('SMS_APP_NAME', 'پارچه') ?? 'پارچه';
|
||||
}
|
||||
|
||||
private async isSmsEventEnabled(
|
||||
key:
|
||||
| 'smsOrderRegistered'
|
||||
| 'smsPricingReady'
|
||||
| 'smsPaymentReminder'
|
||||
| 'smsCheckReview'
|
||||
| 'smsShippingUpdate',
|
||||
): Promise<boolean> {
|
||||
const settings = await this.prisma.systemSettings.findUnique({
|
||||
where: { id: 'default' },
|
||||
select: {
|
||||
smsOrderRegistered: true,
|
||||
smsPricingReady: true,
|
||||
smsPaymentReminder: true,
|
||||
smsCheckReview: true,
|
||||
smsShippingUpdate: true,
|
||||
},
|
||||
});
|
||||
// در نبود ردیف تنظیمات، پیشفرضهای اسکیمای فعال در نظر گرفته میشوند
|
||||
return settings ? settings[key] : true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { Cron, CronExpression } from '@nestjs/schedule';
|
||||
import { OrderStatus } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
|
||||
/** وضعیتهایی که فاکتور نقدی در آنها منتظر پرداخت مشتری است. */
|
||||
const PAYABLE_STATUSES: OrderStatus[] = [
|
||||
OrderStatus.awaiting_cash_payment,
|
||||
OrderStatus.cash_invoice_issued,
|
||||
];
|
||||
|
||||
/** چقدر پیش از پایان مهلت، یادآوری فرستاده شود. */
|
||||
const REMINDER_WINDOW_MS = 60 * 60 * 1000;
|
||||
|
||||
@Injectable()
|
||||
export class CashInvoiceSchedulerService {
|
||||
private readonly logger = new Logger(CashInvoiceSchedulerService.name);
|
||||
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly notifications: NotificationsService,
|
||||
) {}
|
||||
|
||||
@Cron(CronExpression.EVERY_10_MINUTES)
|
||||
async handleCashInvoices(): Promise<void> {
|
||||
await this.sendReminders();
|
||||
await this.expireOverdueInvoices();
|
||||
}
|
||||
|
||||
/** یادآوری یکبار برای هر فاکتوری که کمتر از یک ساعت به پایان مهلتش مانده. */
|
||||
async sendReminders(): Promise<number> {
|
||||
const now = new Date();
|
||||
const soon = new Date(now.getTime() + REMINDER_WINDOW_MS);
|
||||
|
||||
try {
|
||||
const orders = await this.prisma.order.findMany({
|
||||
where: {
|
||||
status: { in: PAYABLE_STATUSES },
|
||||
cashReminderSentAt: null,
|
||||
cashInvoiceExpiresAt: { gt: now, lte: soon },
|
||||
},
|
||||
select: { id: true, finalAmount: true },
|
||||
});
|
||||
|
||||
for (const order of orders) {
|
||||
await this.prisma.order.update({
|
||||
where: { id: order.id },
|
||||
data: { cashReminderSentAt: now },
|
||||
});
|
||||
await this.notifications.notifyOrderEvent(
|
||||
order.id,
|
||||
'cash_invoice_reminder',
|
||||
{ amount: order.finalAmount },
|
||||
);
|
||||
}
|
||||
return orders.length;
|
||||
} catch (err) {
|
||||
this.logger.error(
|
||||
'sendReminders failed',
|
||||
err instanceof Error ? err.stack : err,
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** فاکتورهای نقدی که مهلتشان تمام شده به وضعیت منقضی میروند. */
|
||||
async expireOverdueInvoices(): Promise<number> {
|
||||
const now = new Date();
|
||||
|
||||
try {
|
||||
const orders = await this.prisma.order.findMany({
|
||||
where: {
|
||||
status: { in: PAYABLE_STATUSES },
|
||||
cashInvoiceExpiresAt: { lt: now },
|
||||
},
|
||||
select: { id: true, orderNumber: true, status: true },
|
||||
});
|
||||
|
||||
for (const order of orders) {
|
||||
await this.prisma.$transaction([
|
||||
this.prisma.order.update({
|
||||
where: { id: order.id },
|
||||
data: { status: OrderStatus.expired },
|
||||
}),
|
||||
this.prisma.orderStatusHistory.create({
|
||||
data: {
|
||||
orderId: order.id,
|
||||
fromStatus: order.status,
|
||||
toStatus: OrderStatus.expired,
|
||||
changedBy: 'system',
|
||||
note: 'پایان مهلت پرداخت فاکتور نقدی',
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
await this.notifications.notifyOrderEvent(
|
||||
order.id,
|
||||
'cash_invoice_expired',
|
||||
);
|
||||
this.notifications.dispatchAdmin({
|
||||
title: 'فاکتور نقدی منقضی شد',
|
||||
body: `مهلت پرداخت سفارش ${order.orderNumber} به پایان رسید و سفارش منقضی شد.`,
|
||||
link: '/orders',
|
||||
});
|
||||
}
|
||||
|
||||
if (orders.length) {
|
||||
this.logger.log(`${orders.length} فاکتور نقدی منقضی شد`);
|
||||
}
|
||||
return orders.length;
|
||||
} catch (err) {
|
||||
this.logger.error(
|
||||
'expireOverdueInvoices failed',
|
||||
err instanceof Error ? err.stack : err,
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
ArrayMinSize,
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsDateString,
|
||||
IsEnum,
|
||||
IsNumber,
|
||||
@@ -94,10 +95,14 @@ export class SubmitOrderDto {
|
||||
@IsString()
|
||||
companyId!: string;
|
||||
|
||||
/**
|
||||
* روش تسویه بعد از قیمتگذاری ادمین انتخاب میشود، نه اینجا.
|
||||
* برای سازگاری با کلاینتهای قدیمی پذیرفته و نادیده گرفته میشود.
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsEnum(SettlementType)
|
||||
settlementType!: SettlementType;
|
||||
settlementType?: SettlementType;
|
||||
|
||||
/** Alias پذیرفتهشده از کلاینتهای قدیمی؛ نادیده گرفته میشود */
|
||||
@IsOptional()
|
||||
@IsEnum(SettlementType)
|
||||
settlementMethod?: SettlementType;
|
||||
@@ -123,6 +128,15 @@ export class SubmitOrderDto {
|
||||
checks?: SubmitOrderCheckDto[];
|
||||
}
|
||||
|
||||
export class ChooseSettlementDto {
|
||||
@IsEnum(SettlementType)
|
||||
settlementType!: SettlementType;
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(CheckRegistrationMode)
|
||||
checkMode?: CheckRegistrationMode;
|
||||
}
|
||||
|
||||
export class AdminCreateOrderDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@@ -229,6 +243,12 @@ export class UpdateOrderStatusDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
reason?: string;
|
||||
|
||||
/** عبور از ماشین وضعیت؛ فقط با ذکر دلیل و با ثبت در تاریخچه. */
|
||||
@IsOptional()
|
||||
@Type(() => Boolean)
|
||||
@IsBoolean()
|
||||
force?: boolean;
|
||||
}
|
||||
|
||||
export class UpdateOrderChecksDto {
|
||||
|
||||
@@ -13,6 +13,7 @@ import { CurrentUser } from '../common/decorators/current-user.decorator';
|
||||
import type { AuthPrincipal } from '../common/decorators/current-user.decorator';
|
||||
import {
|
||||
AdminCreateOrderDto,
|
||||
ChooseSettlementDto,
|
||||
SubmitOrderDto,
|
||||
UpdateOrderChecksDto,
|
||||
UpdateOrderPricingDto,
|
||||
@@ -41,6 +42,16 @@ export class CustomerOrdersController {
|
||||
return this.ordersService.submitOrder(user.id, dto);
|
||||
}
|
||||
|
||||
@AuthType('customer')
|
||||
@Post(':id/settlement')
|
||||
chooseSettlement(
|
||||
@CurrentUser() user: AuthPrincipal,
|
||||
@Param('id') id: string,
|
||||
@Body() dto: ChooseSettlementDto,
|
||||
) {
|
||||
return this.ordersService.chooseSettlement(user.id, id, dto);
|
||||
}
|
||||
|
||||
@AuthType('customer')
|
||||
@Patch(':id/checks')
|
||||
updateChecks(
|
||||
|
||||
@@ -4,10 +4,13 @@ import {
|
||||
CustomerOrdersController,
|
||||
} from './orders.controller';
|
||||
import { OrdersService } from './orders.service';
|
||||
import { CashInvoiceSchedulerService } from './cash-invoice-scheduler.service';
|
||||
import { NotificationsModule } from '../notifications/notifications.module';
|
||||
|
||||
@Module({
|
||||
imports: [NotificationsModule],
|
||||
controllers: [CustomerOrdersController, AdminOrdersController],
|
||||
providers: [OrdersService],
|
||||
providers: [OrdersService, CashInvoiceSchedulerService],
|
||||
exports: [OrdersService],
|
||||
})
|
||||
export class OrdersModule {}
|
||||
|
||||
+169
-84
@@ -13,6 +13,7 @@ import {
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import {
|
||||
AdminCreateOrderDto,
|
||||
ChooseSettlementDto,
|
||||
SubmitOrderCheckDto,
|
||||
SubmitOrderDto,
|
||||
UpdateOrderChecksDto,
|
||||
@@ -26,6 +27,13 @@ import {
|
||||
resolveAdminStatusFilter,
|
||||
toAdminStatus,
|
||||
} from '../common/utils/order-status';
|
||||
import {
|
||||
allowedTransitions,
|
||||
assertTransition,
|
||||
} from '../common/utils/order-transitions';
|
||||
import { resolveMaxMeterage } from '../common/utils/meterage';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
import { eventForStatus } from '../notifications/notification-events';
|
||||
import { joinFullName, splitFullName } from '../common/utils/name';
|
||||
import {
|
||||
isValidPhone,
|
||||
@@ -52,19 +60,27 @@ function roundMoney(value: number): number {
|
||||
|
||||
/** بعد از تأیید چکها یا ارسال سفارش، مشتری نباید بتواند چکها را بازنویسی کند. */
|
||||
const CHECK_EDITABLE_STATUSES: OrderStatus[] = [
|
||||
OrderStatus.awaiting_review,
|
||||
OrderStatus.pricing,
|
||||
OrderStatus.needs_correction,
|
||||
OrderStatus.awaiting_settlement_fix,
|
||||
OrderStatus.check_in_progress,
|
||||
OrderStatus.awaiting_check_review,
|
||||
OrderStatus.check_needs_fix,
|
||||
OrderStatus.check_rejected,
|
||||
];
|
||||
|
||||
/**
|
||||
* تا وقتی ادمین فاکتور را نفرستاده، مشتری نباید هیچ مبلغی ببیند.
|
||||
* پنهانسازی روی خود پاسخ API انجام میشود، نه در کلاینت.
|
||||
*/
|
||||
const PRICE_HIDDEN_STATUSES: OrderStatus[] = [
|
||||
OrderStatus.awaiting_review,
|
||||
OrderStatus.pricing,
|
||||
];
|
||||
|
||||
@Injectable()
|
||||
export class OrdersService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly notifications: NotificationsService,
|
||||
) {}
|
||||
|
||||
async findMyOrders(userId: string) {
|
||||
const orders = await this.prisma.order.findMany({
|
||||
@@ -119,12 +135,20 @@ export class OrdersService {
|
||||
}
|
||||
|
||||
async submitOrder(userId: string, dto: SubmitOrderDto) {
|
||||
const company = await this.prisma.company.findUnique({
|
||||
where: { id: dto.companyId },
|
||||
});
|
||||
const [company, settings] = await Promise.all([
|
||||
this.prisma.company.findUnique({ where: { id: dto.companyId } }),
|
||||
this.prisma.systemSettings.findUnique({
|
||||
where: { id: 'default' },
|
||||
select: { maxMeteragePerInvoice: true },
|
||||
}),
|
||||
]);
|
||||
if (!company || !company.isActive) {
|
||||
throw new NotFoundException('شرکت یافت نشد یا غیرفعال است');
|
||||
}
|
||||
const maxMeterage = resolveMaxMeterage(
|
||||
company.maxMeteragePerInvoice,
|
||||
settings?.maxMeteragePerInvoice,
|
||||
);
|
||||
|
||||
const productIds = dto.items.map((i) => i.productId);
|
||||
const products = await this.prisma.product.findMany({
|
||||
@@ -159,44 +183,17 @@ export class OrdersService {
|
||||
productCode: product.code,
|
||||
color: product.color,
|
||||
requestedMeterage: item.meterage,
|
||||
unitPrice: product.basePrice,
|
||||
notes: item.notes?.trim() || null,
|
||||
};
|
||||
});
|
||||
|
||||
if (totalMeterage > company.maxMeteragePerInvoice) {
|
||||
if (totalMeterage > maxMeterage) {
|
||||
throw new BadRequestException(
|
||||
`حداکثر متراژ مجاز هر فاکتور ${company.maxMeteragePerInvoice} متر است`,
|
||||
`حداکثر متراژ مجاز هر فاکتور ${maxMeterage} متر است`,
|
||||
);
|
||||
}
|
||||
|
||||
const settlementType = dto.settlementType;
|
||||
if (settlementType === SettlementType.check) {
|
||||
if (!dto.checks?.length) {
|
||||
throw new BadRequestException('برای تسویه چکی حداقل یک چک لازم است');
|
||||
}
|
||||
}
|
||||
|
||||
const checkMode =
|
||||
settlementType === SettlementType.check
|
||||
? (dto.checkMode ?? CheckRegistrationMode.full)
|
||||
: null;
|
||||
|
||||
const rass =
|
||||
settlementType === SettlementType.check && dto.checks?.length
|
||||
? calculateCheckRass(
|
||||
dto.checks.map((c) => ({
|
||||
amount: c.amount,
|
||||
dueDate: c.dueDate,
|
||||
})),
|
||||
)
|
||||
: null;
|
||||
|
||||
const cashInvoiceExpiresAt =
|
||||
settlementType === SettlementType.cash
|
||||
? computeCashInvoiceExpiry(company)
|
||||
: null;
|
||||
|
||||
// روش تسویه و چکها بعد از قیمتگذاری ادمین گرفته میشوند، نه اینجا.
|
||||
const order = await this.createWithUniqueOrderNumber((orderNumber) =>
|
||||
this.prisma.$transaction(async (tx) => {
|
||||
const created = await tx.order.create({
|
||||
@@ -205,36 +202,19 @@ export class OrdersService {
|
||||
userId,
|
||||
companyId: company.id,
|
||||
companyName: company.name,
|
||||
settlementType,
|
||||
checkMode,
|
||||
settlementType: null,
|
||||
checkMode: null,
|
||||
status: OrderStatus.awaiting_review,
|
||||
totalMeterage,
|
||||
notes: dto.notes?.trim() || null,
|
||||
checkRassDays: rass?.rassDays ?? null,
|
||||
checkRassDate: rass?.rassDate ? new Date(rass.rassDate) : null,
|
||||
cashInvoiceExpiresAt,
|
||||
items: { create: itemsData },
|
||||
checks:
|
||||
settlementType === SettlementType.check && dto.checks
|
||||
? {
|
||||
create: dto.checks.map((c, index) =>
|
||||
this.mapCheckCreate(c, index + 1, checkMode!),
|
||||
),
|
||||
}
|
||||
: undefined,
|
||||
statusHistory: {
|
||||
create: [
|
||||
{
|
||||
fromStatus: null,
|
||||
toStatus: OrderStatus.submitted,
|
||||
changedBy: 'customer',
|
||||
note: 'سفارش ثبت شد',
|
||||
},
|
||||
{
|
||||
fromStatus: OrderStatus.submitted,
|
||||
toStatus: OrderStatus.awaiting_review,
|
||||
changedBy: 'customer',
|
||||
note: 'ارسال برای بررسی مدیریت',
|
||||
note: 'سفارش ثبت شد و برای بررسی مدیریت ارسال شد',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -254,15 +234,69 @@ export class OrdersService {
|
||||
}),
|
||||
);
|
||||
|
||||
this.notifications.dispatchOrderEvent(order.id, 'order_submitted');
|
||||
this.notifications.dispatchAdmin({
|
||||
title: 'سفارش جدید ثبت شد',
|
||||
body: `سفارش ${order.orderNumber} با ${order.items.length} قلم و ${order.totalMeterage} متر در انتظار قیمتگذاری است.`,
|
||||
link: `/orders/${order.id}`,
|
||||
});
|
||||
|
||||
return this.mapOrder(order);
|
||||
}
|
||||
|
||||
async adminCreate(adminId: string, dto: AdminCreateOrderDto) {
|
||||
const settlementType = dto.settlementType ?? dto.settlementMethod;
|
||||
if (!settlementType) {
|
||||
throw new BadRequestException('نوع تسویه الزامی است');
|
||||
/**
|
||||
* مشتری بعد از دیدن جمع فاکتور روش تسویه را انتخاب میکند.
|
||||
* نقدی مستقیم به مرحله پرداخت میرود، چکی به مرحله ثبت چکها.
|
||||
*/
|
||||
async chooseSettlement(userId: string, id: string, dto: ChooseSettlementDto) {
|
||||
const order = await this.getOrder(id);
|
||||
if (order.userId !== userId) {
|
||||
throw new ForbiddenException('دسترسی به این سفارش مجاز نیست');
|
||||
}
|
||||
if (order.status !== OrderStatus.awaiting_settlement) {
|
||||
throw new BadRequestException(
|
||||
'انتخاب روش تسویه فقط پس از صدور فاکتور توسط مدیریت امکانپذیر است',
|
||||
);
|
||||
}
|
||||
|
||||
const isCash = dto.settlementType === SettlementType.cash;
|
||||
const nextStatus = isCash
|
||||
? OrderStatus.awaiting_cash_payment
|
||||
: OrderStatus.check_in_progress;
|
||||
assertTransition(order.status, nextStatus);
|
||||
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
await tx.order.update({
|
||||
where: { id },
|
||||
data: {
|
||||
settlementType: dto.settlementType,
|
||||
checkMode: isCash
|
||||
? null
|
||||
: (dto.checkMode ?? CheckRegistrationMode.full),
|
||||
cashInvoiceExpiresAt: isCash
|
||||
? computeCashInvoiceExpiry(order.company)
|
||||
: null,
|
||||
cashReminderSentAt: null,
|
||||
status: nextStatus,
|
||||
},
|
||||
});
|
||||
await tx.orderStatusHistory.create({
|
||||
data: {
|
||||
orderId: id,
|
||||
fromStatus: order.status,
|
||||
toStatus: nextStatus,
|
||||
changedBy: 'customer',
|
||||
note: isCash
|
||||
? 'مشتری تسویه نقدی را انتخاب کرد'
|
||||
: 'مشتری تسویه چکی را انتخاب کرد',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return this.findMyOrder(userId, id);
|
||||
}
|
||||
|
||||
async adminCreate(adminId: string, dto: AdminCreateOrderDto) {
|
||||
const company = await this.prisma.company.findUnique({
|
||||
where: { id: dto.companyId },
|
||||
});
|
||||
@@ -303,7 +337,7 @@ export class OrdersService {
|
||||
userId: ownerId,
|
||||
companyId: company.id,
|
||||
companyName: company.name,
|
||||
settlementType,
|
||||
settlementType: null,
|
||||
status: OrderStatus.awaiting_review,
|
||||
totalMeterage,
|
||||
notes: dto.notes?.trim() || null,
|
||||
@@ -422,6 +456,26 @@ export class OrdersService {
|
||||
return this.mapOrder(order, true);
|
||||
}
|
||||
|
||||
if (dto.force) {
|
||||
if (!dto.reason?.trim()) {
|
||||
throw new BadRequestException(
|
||||
'برای تغییر دستی وضعیت، ذکر دلیل الزامی است',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
assertTransition(order.status, dto.status);
|
||||
}
|
||||
|
||||
// فاکتور بدون مبلغ نباید برای مشتری ارسال شود
|
||||
if (
|
||||
dto.status === OrderStatus.awaiting_settlement &&
|
||||
(order.finalAmount ?? 0) <= 0
|
||||
) {
|
||||
throw new BadRequestException(
|
||||
'ابتدا سفارش را قیمتگذاری کنید تا فاکتور قابل ارسال باشد',
|
||||
);
|
||||
}
|
||||
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
await tx.order.update({
|
||||
where: { id },
|
||||
@@ -433,7 +487,9 @@ export class OrdersService {
|
||||
fromStatus: order.status,
|
||||
toStatus: dto.status,
|
||||
changedBy: `admin:${adminId}`,
|
||||
note: dto.note || null,
|
||||
note: dto.force
|
||||
? `تغییر دستی وضعیت${dto.note ? ` — ${dto.note}` : ''}`
|
||||
: dto.note || null,
|
||||
reason: dto.reason || null,
|
||||
},
|
||||
});
|
||||
@@ -454,6 +510,13 @@ export class OrdersService {
|
||||
}
|
||||
});
|
||||
|
||||
const event = eventForStatus(dto.status);
|
||||
if (event) {
|
||||
this.notifications.dispatchOrderEvent(id, event, {
|
||||
note: dto.reason ?? dto.note ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
return this.findAdminOrder(id);
|
||||
}
|
||||
|
||||
@@ -471,6 +534,10 @@ export class OrdersService {
|
||||
);
|
||||
}
|
||||
|
||||
if (!dto.checks.length) {
|
||||
throw new BadRequestException('حداقل یک چک لازم است');
|
||||
}
|
||||
|
||||
const checkMode =
|
||||
dto.checkMode ?? order.checkMode ?? CheckRegistrationMode.full;
|
||||
const rass = calculateCheckRass(
|
||||
@@ -478,6 +545,16 @@ export class OrdersService {
|
||||
order.createdAt,
|
||||
);
|
||||
|
||||
// ثبت اولیه و اصلاح، هر دو سفارش را به صف بررسی چک میبرند.
|
||||
const goesToReview =
|
||||
order.status === OrderStatus.check_in_progress ||
|
||||
order.status === OrderStatus.check_needs_fix ||
|
||||
order.status === OrderStatus.check_rejected;
|
||||
const nextStatus = goesToReview
|
||||
? OrderStatus.awaiting_check_review
|
||||
: order.status;
|
||||
const isFix = order.status !== OrderStatus.check_in_progress;
|
||||
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
await tx.check.deleteMany({ where: { orderId: id } });
|
||||
await tx.check.createMany({
|
||||
@@ -492,25 +569,32 @@ export class OrdersService {
|
||||
checkMode,
|
||||
checkRassDays: rass.rassDays,
|
||||
checkRassDate: rass.rassDate ? new Date(rass.rassDate) : null,
|
||||
status:
|
||||
order.status === OrderStatus.check_needs_fix
|
||||
? OrderStatus.awaiting_check_review
|
||||
: order.status,
|
||||
status: nextStatus,
|
||||
},
|
||||
});
|
||||
if (order.status === OrderStatus.check_needs_fix) {
|
||||
if (nextStatus !== order.status) {
|
||||
await tx.orderStatusHistory.create({
|
||||
data: {
|
||||
orderId: id,
|
||||
fromStatus: order.status,
|
||||
toStatus: OrderStatus.awaiting_check_review,
|
||||
toStatus: nextStatus,
|
||||
changedBy: 'customer',
|
||||
note: 'چکها اصلاح و مجدداً ارسال شد',
|
||||
note: isFix
|
||||
? 'چکها اصلاح و مجدداً ارسال شد'
|
||||
: 'چکها برای بررسی ارسال شد',
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (nextStatus !== order.status) {
|
||||
this.notifications.dispatchAdmin({
|
||||
title: isFix ? 'چکهای اصلاحشده رسید' : 'چکهای سفارش ثبت شد',
|
||||
body: `مشتری ${dto.checks.length} چک برای سفارش ${order.orderNumber} ثبت کرد و منتظر بررسی است.`,
|
||||
link: `/orders/${id}`,
|
||||
});
|
||||
}
|
||||
|
||||
return this.findMyOrder(userId, id);
|
||||
}
|
||||
|
||||
@@ -579,6 +663,8 @@ export class OrdersService {
|
||||
order.user.firstName,
|
||||
order.user.lastName,
|
||||
);
|
||||
const hidePrices =
|
||||
!adminView && PRICE_HIDDEN_STATUSES.includes(order.status);
|
||||
const base = {
|
||||
id: order.id,
|
||||
orderNumber: order.orderNumber,
|
||||
@@ -590,12 +676,14 @@ export class OrdersService {
|
||||
checkMode: order.checkMode ?? undefined,
|
||||
status: order.status,
|
||||
adminStatus: toAdminStatus(order.status),
|
||||
allowedTransitions: adminView ? allowedTransitions(order.status) : [],
|
||||
totalMeterage: order.totalMeterage,
|
||||
meterage: order.totalMeterage,
|
||||
totalAmount: order.totalAmount,
|
||||
shippingCost: order.shippingCost,
|
||||
totalDiscount: order.totalDiscount,
|
||||
finalAmount: order.finalAmount,
|
||||
totalAmount: hidePrices ? null : order.totalAmount,
|
||||
shippingCost: hidePrices ? null : order.shippingCost,
|
||||
totalDiscount: hidePrices ? null : order.totalDiscount,
|
||||
finalAmount: hidePrices ? null : order.finalAmount,
|
||||
pricesVisible: !hidePrices,
|
||||
notes: order.notes ?? undefined,
|
||||
checkRassDays: order.checkRassDays ?? undefined,
|
||||
checkRassDate: order.checkRassDate?.toISOString(),
|
||||
@@ -615,12 +703,12 @@ export class OrdersService {
|
||||
color: item.color,
|
||||
requestedMeterage: item.requestedMeterage,
|
||||
confirmedMeterage: item.confirmedMeterage,
|
||||
unitPrice: item.unitPrice,
|
||||
supplyPrice: item.unitPrice,
|
||||
unitDiscount: item.unitDiscount ?? 0,
|
||||
discount: item.unitDiscount ?? 0,
|
||||
lineTotal: item.lineTotal,
|
||||
totalAmount: item.lineTotal,
|
||||
unitPrice: hidePrices ? null : item.unitPrice,
|
||||
supplyPrice: hidePrices ? null : item.unitPrice,
|
||||
unitDiscount: hidePrices ? null : (item.unitDiscount ?? 0),
|
||||
discount: hidePrices ? null : (item.unitDiscount ?? 0),
|
||||
lineTotal: hidePrices ? null : item.lineTotal,
|
||||
totalAmount: hidePrices ? null : item.lineTotal,
|
||||
notes: item.notes ?? undefined,
|
||||
})),
|
||||
checks: order.checks.map((c) => ({
|
||||
@@ -688,9 +776,6 @@ export class OrdersService {
|
||||
})),
|
||||
};
|
||||
|
||||
if (adminView) {
|
||||
return base;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,10 @@ export class UpdateSettingsDto {
|
||||
@IsBoolean()
|
||||
smsOrderRegistered?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
smsPricingReady?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
smsPaymentReminder?: boolean;
|
||||
|
||||
@@ -21,6 +21,7 @@ export class SettingsService {
|
||||
defaultSettlementCheck: dto.defaultSettlementCheck,
|
||||
defaultSettlementCash: dto.defaultSettlementCash,
|
||||
smsOrderRegistered: dto.smsOrderRegistered,
|
||||
smsPricingReady: dto.smsPricingReady,
|
||||
smsPaymentReminder: dto.smsPaymentReminder,
|
||||
smsCheckReview: dto.smsCheckReview,
|
||||
smsShippingUpdate: dto.smsShippingUpdate,
|
||||
@@ -52,6 +53,7 @@ export class SettingsService {
|
||||
defaultSettlementCheck: boolean;
|
||||
defaultSettlementCash: boolean;
|
||||
smsOrderRegistered: boolean;
|
||||
smsPricingReady: boolean;
|
||||
smsPaymentReminder: boolean;
|
||||
smsCheckReview: boolean;
|
||||
smsShippingUpdate: boolean;
|
||||
@@ -66,6 +68,7 @@ export class SettingsService {
|
||||
defaultSettlementCheck: settings.defaultSettlementCheck,
|
||||
defaultSettlementCash: settings.defaultSettlementCash,
|
||||
smsOrderRegistered: settings.smsOrderRegistered,
|
||||
smsPricingReady: settings.smsPricingReady,
|
||||
smsPaymentReminder: settings.smsPaymentReminder,
|
||||
smsCheckReview: settings.smsCheckReview,
|
||||
smsShippingUpdate: settings.smsShippingUpdate,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user