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>
212 lines
8.9 KiB
JavaScript
212 lines
8.9 KiB
JavaScript
"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);
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.ShipmentsService = void 0;
|
|
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,
|
|
client_1.ShipmentStatus.shipped,
|
|
client_1.ShipmentStatus.delivered,
|
|
];
|
|
const SHIPMENT_TO_ORDER = {
|
|
preparing: client_1.OrderStatus.preparing,
|
|
ready: client_1.OrderStatus.ready_to_ship,
|
|
shipped: client_1.OrderStatus.shipped,
|
|
delivered: client_1.OrderStatus.delivered,
|
|
};
|
|
let ShipmentsService = class ShipmentsService {
|
|
prisma;
|
|
notifications;
|
|
constructor(prisma, notifications) {
|
|
this.prisma = prisma;
|
|
this.notifications = notifications;
|
|
}
|
|
async findAll(status) {
|
|
const shipments = await this.prisma.shipment.findMany({
|
|
where: status ? { status } : undefined,
|
|
orderBy: { createdAt: 'desc' },
|
|
include: {
|
|
order: { include: { user: true } },
|
|
},
|
|
});
|
|
return shipments.map((s) => this.map(s));
|
|
}
|
|
async findOne(id) {
|
|
const shipment = await this.prisma.shipment.findUnique({
|
|
where: { id },
|
|
include: { order: { include: { user: true } } },
|
|
});
|
|
if (!shipment)
|
|
throw new common_1.NotFoundException('ارسال یافت نشد');
|
|
return this.map(shipment);
|
|
}
|
|
async create(adminId, dto) {
|
|
const order = await this.prisma.order.findUnique({
|
|
where: { id: dto.orderId },
|
|
include: { shipment: true, user: true },
|
|
});
|
|
if (!order)
|
|
throw new common_1.NotFoundException('سفارش یافت نشد');
|
|
if (order.shipment) {
|
|
throw new common_1.ConflictException('برای این سفارش قبلاً ارسال ثبت شده است');
|
|
}
|
|
const status = dto.status ?? client_1.ShipmentStatus.preparing;
|
|
const shipment = await this.prisma.$transaction(async (tx) => {
|
|
const created = await tx.shipment.create({
|
|
data: {
|
|
orderId: order.id,
|
|
carrier: dto.carrier.trim(),
|
|
trackingNumber: dto.trackingNumber?.trim() || null,
|
|
meterage: dto.meterage ?? order.totalMeterage,
|
|
status,
|
|
shippedAt: status === client_1.ShipmentStatus.shipped ? new Date() : null,
|
|
deliveredAt: status === client_1.ShipmentStatus.delivered ? new Date() : null,
|
|
},
|
|
include: { order: { include: { user: true } } },
|
|
});
|
|
const nextOrderStatus = SHIPMENT_TO_ORDER[status];
|
|
if (order.status !== nextOrderStatus) {
|
|
await tx.order.update({
|
|
where: { id: order.id },
|
|
data: { status: nextOrderStatus },
|
|
});
|
|
await tx.orderStatusHistory.create({
|
|
data: {
|
|
orderId: order.id,
|
|
fromStatus: order.status,
|
|
toStatus: nextOrderStatus,
|
|
changedBy: `admin:${adminId}`,
|
|
note: 'ثبت ارسال',
|
|
},
|
|
});
|
|
}
|
|
return created;
|
|
});
|
|
this.notifyOrderStatus(order.id, SHIPMENT_TO_ORDER[status], {
|
|
carrier: shipment.carrier,
|
|
trackingNumber: shipment.trackingNumber,
|
|
});
|
|
return this.map(shipment);
|
|
}
|
|
async update(id, dto) {
|
|
await this.ensure(id);
|
|
const shipment = await this.prisma.shipment.update({
|
|
where: { id },
|
|
data: {
|
|
carrier: dto.carrier?.trim(),
|
|
trackingNumber: dto.trackingNumber === undefined
|
|
? undefined
|
|
: dto.trackingNumber?.trim() || null,
|
|
meterage: dto.meterage,
|
|
status: dto.status,
|
|
},
|
|
include: { order: { include: { user: true } } },
|
|
});
|
|
return this.map(shipment);
|
|
}
|
|
async advanceStatus(id, adminId, dto) {
|
|
const shipment = await this.prisma.shipment.findUnique({
|
|
where: { id },
|
|
include: { order: { include: { user: true } } },
|
|
});
|
|
if (!shipment)
|
|
throw new common_1.NotFoundException('ارسال یافت نشد');
|
|
let nextStatus = dto.status;
|
|
if (!nextStatus) {
|
|
const idx = STATUS_FLOW.indexOf(shipment.status);
|
|
if (idx < 0 || idx >= STATUS_FLOW.length - 1) {
|
|
throw new common_1.BadRequestException('وضعیت ارسال قابل پیشرفت نیست');
|
|
}
|
|
nextStatus = STATUS_FLOW[idx + 1];
|
|
}
|
|
const updated = await this.prisma.$transaction(async (tx) => {
|
|
const result = await tx.shipment.update({
|
|
where: { id },
|
|
data: {
|
|
status: nextStatus,
|
|
trackingNumber: dto.trackingNumber !== undefined
|
|
? dto.trackingNumber.trim() || null
|
|
: undefined,
|
|
shippedAt: nextStatus === client_1.ShipmentStatus.shipped
|
|
? (shipment.shippedAt ?? new Date())
|
|
: shipment.shippedAt,
|
|
deliveredAt: nextStatus === client_1.ShipmentStatus.delivered
|
|
? new Date()
|
|
: shipment.deliveredAt,
|
|
},
|
|
include: { order: { include: { user: true } } },
|
|
});
|
|
const nextOrderStatus = SHIPMENT_TO_ORDER[nextStatus];
|
|
if (shipment.order.status !== nextOrderStatus) {
|
|
await tx.order.update({
|
|
where: { id: shipment.orderId },
|
|
data: { status: nextOrderStatus },
|
|
});
|
|
await tx.orderStatusHistory.create({
|
|
data: {
|
|
orderId: shipment.orderId,
|
|
fromStatus: shipment.order.status,
|
|
toStatus: nextOrderStatus,
|
|
changedBy: `admin:${adminId}`,
|
|
note: `وضعیت ارسال: ${nextStatus}`,
|
|
},
|
|
});
|
|
}
|
|
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)
|
|
throw new common_1.NotFoundException('ارسال یافت نشد');
|
|
return shipment;
|
|
}
|
|
map(s) {
|
|
return {
|
|
id: s.id,
|
|
orderId: s.orderId,
|
|
orderNumber: s.order.orderNumber,
|
|
customerName: (0, name_1.joinFullName)(s.order.user.firstName, s.order.user.lastName),
|
|
carrier: s.carrier,
|
|
trackingNumber: s.trackingNumber ?? '',
|
|
meterage: s.meterage,
|
|
status: s.status,
|
|
shippedAt: s.shippedAt?.toISOString() ?? null,
|
|
deliveredAt: s.deliveredAt?.toISOString() ?? null,
|
|
createdAt: s.createdAt.toISOString(),
|
|
updatedAt: s.updatedAt.toISOString(),
|
|
};
|
|
}
|
|
};
|
|
exports.ShipmentsService = ShipmentsService;
|
|
exports.ShipmentsService = ShipmentsService = __decorate([
|
|
(0, common_1.Injectable)(),
|
|
__metadata("design:paramtypes", [prisma_service_1.PrismaService,
|
|
notifications_service_1.NotificationsService])
|
|
], ShipmentsService);
|
|
//# sourceMappingURL=shipments.service.js.map
|