@@ -4,15 +4,24 @@ import { Customer } from '../models/Customer';
44import { SellRepository } from '../repositories/SellRepository' ;
55import { BillRepository } from '../repositories/BillRepository' ;
66import { CustomerRepository } from '../repositories/CustomerRepository' ;
7- import { ProductService } from '../services/ProductService' ;
87import { GenericService } from './GenericService' ;
98import { Cache } from '../utils/cacheUtil' ;
109import logger from '../utils/logger' ;
1110import { inject , injectable } from 'tsyringe' ;
1211import { BaseAppException } from '../errors/BaseAppException' ;
13- import { uploadFile } from '../utils/s3Util' ; // S3 utility
14- import { sendEmail } from '../utils/sesUtil' ; // SES utility
15- import { customer_receipt } from '../email_templates/templates' ; // hypothetical utility to format bill
12+ import { uploadFile } from '../utils/s3Util' ;
13+ import { sendEmail } from '../utils/sesUtil' ;
14+ import { customer_receipt } from '../email_templates/templates' ;
15+ import { ProductRepository } from '../repositories/ProductRepository' ;
16+
17+ export interface ProcessSalesDTO {
18+ customerId : number ;
19+ sales : Array < {
20+ productId : number ;
21+ quantity : number ;
22+ sale_price : number ;
23+ } > ;
24+ }
1625
1726@injectable ( )
1827export class SellService extends GenericService < Sale > {
@@ -22,98 +31,129 @@ export class SellService extends GenericService<Sale> {
2231 @inject ( 'BillRepository' ) private readonly billRepository : BillRepository ,
2332 @inject ( 'CustomerRepository' )
2433 private readonly customerRepository : CustomerRepository ,
25- @inject ( 'ProductService' ) private readonly productService : ProductService ,
34+ @inject ( 'ProductRepository' )
35+ private readonly productRepository : ProductRepository ,
2636 ) {
2737 super ( cache , sellRepository , Sale ) ;
2838 logger . info ( '✅ [SellService] Initialized SellService' ) ;
2939 this . sellRepository = sellRepository ;
3040 this . billRepository = billRepository ;
3141 this . customerRepository = customerRepository ;
32- this . productService = productService ;
42+ this . productRepository = productRepository ;
3343 }
3444
35- async processSale (
36- customerData : Omit < Customer , 'id' | 'bills' > ,
37- sales : { productId : number ; quantity : number ; salePrice : number } [ ] ,
38- ) : Promise < Bill > {
45+ async processSales ( salesDTO : ProcessSalesDTO ) : Promise < boolean > {
46+ logger . info ( '🚀 Starting processSales...' ) ;
47+
48+ // 1️⃣ Retrieve the customer
49+ const customer : Customer | null =
50+ await this . customerRepository . findEntityById ( salesDTO . customerId ) ;
51+ if ( ! customer ) {
52+ logger . error ( '❌ Customer not found with id: %s' , salesDTO . customerId ) ;
53+ throw new BaseAppException ( 'Customer not found' ) ;
54+ }
3955 logger . info (
40- `🛒 [SellService] Processing sale for new customer: ${ customerData . email } ` ,
56+ '👤 Found customer: %s %s' ,
57+ customer . first_name ,
58+ customer . last_name ,
4159 ) ;
4260
43- const newCustomer = new Customer ( ) ;
44- Object . assign ( newCustomer , customerData ) ;
45- const savedCustomer =
46- await this . customerRepository . createEntity ( newCustomer ) ;
47-
48- const newBill = new Bill ( ) ;
49- newBill . customer = savedCustomer ! ;
50- newBill . total_amount = 0 ;
51- newBill . sales = [ ] ;
52-
53- const savedBill = await this . billRepository . createEntity ( newBill ) ;
54-
55- let totalAmount = 0 ;
56-
57- for ( const sale of sales ) {
58- const product = await this . productService . findById ( sale . productId ) ;
59- if ( ! product || product . available_quantity < sale . quantity ) {
61+ // 2️⃣ Create a new Bill for the customer
62+ const bill = new Bill ( ) ;
63+ bill . customer = customer ;
64+ bill . total_amount = 0 ;
65+ bill . sales = [ ] ;
66+ logger . info ( '🧾 New bill created for customer' ) ;
67+
68+ // 3️⃣ Process each sale item
69+ for ( const saleDTO of salesDTO . sales ) {
70+ logger . info (
71+ '🔍 Processing sale item for product id: %s' ,
72+ saleDTO . productId ,
73+ ) ;
74+
75+ const product = await this . productRepository . findEntityById (
76+ saleDTO . productId ,
77+ ) ;
78+ if ( ! product ) {
79+ logger . error ( '❌ Product not found with id: %s' , saleDTO . productId ) ;
6080 throw new BaseAppException (
61- `❌ Insufficient stock or invalid product ID: ${ sale . productId } ` ,
81+ `Product with id ${ saleDTO . productId } not found ` ,
6282 ) ;
6383 }
84+ logger . info ( '📦 Found product: %s' , product . name ) ;
6485
65- const newSale = new Sale ( ) ;
66- newSale . bill = savedBill ! ;
67- newSale . product = product ;
68- newSale . quantity = sale . quantity ;
69- newSale . sale_price = sale . salePrice ;
86+ if ( product . available_quantity < saleDTO . quantity ) {
87+ logger . error ( '❌ Insufficient stock for product: %s' , product . name ) ;
88+ throw new BaseAppException (
89+ `Insufficient stock for product ${ product . name } ` ,
90+ ) ;
91+ }
7092
71- await this . sellRepository . createEntity ( newSale ) ;
72- newBill . sales . push ( newSale ) ;
93+ // Update product stock
94+ product . available_quantity -= saleDTO . quantity ;
95+ await this . productRepository . updateEntity ( saleDTO . productId , product ) ;
96+ logger . info (
97+ '✅ Updated stock for product %s, new quantity: %s' ,
98+ product . name ,
99+ product . available_quantity ,
100+ ) ;
101+
102+ // Create and add Sale
103+ const sale = new Sale ( ) ;
104+ sale . bill = bill ;
105+ sale . product = product ;
106+ sale . quantity = saleDTO . quantity ;
107+ sale . sale_price = saleDTO . sale_price ;
108+ bill . sales . push ( sale ) ;
109+ bill . total_amount += sale . sale_price * sale . quantity ;
110+ logger . info (
111+ '🛍️ Added sale item: %s units of %s at $%s each' ,
112+ sale . quantity ,
113+ product . name ,
114+ sale . sale_price ,
115+ ) ;
116+ }
73117
74- totalAmount += sale . quantity * sale . salePrice ;
118+ // 4️⃣ Save the Bill (cascading the sales)
119+ const savedBill = await this . billRepository . createEntity ( bill ) ;
75120
76- await this . productService . update ( product . id , {
77- available_quantity : product . available_quantity - sale . quantity ,
78- } ) ;
121+ if ( ! savedBill ) {
122+ logger . error ( '❌ Error saving bill' ) ;
123+ throw new BaseAppException ( 'Error saving bill' ) ;
79124 }
80125
81- savedBill ! . total_amount = totalAmount ;
82- const finalBill = await this . billRepository . updateEntity ( savedBill ! . id , {
83- total_amount : totalAmount ,
84- } ) ;
126+ logger . info (
127+ '💾 Bill saved with id: %s, Total amount: $%s' ,
128+ savedBill . id ,
129+ savedBill . total_amount ,
130+ ) ;
85131
86- // 🧾 Upload the bill HTML to S3
87- const billHtml = customer_receipt ( finalBill ! ) ;
88- const fileName = `bill-${ finalBill ! . id } .html` ;
132+ // 5️⃣ Generate a receipt and upload it to S3
133+ const billHtml = customer_receipt ( savedBill ) ;
134+ const fileName = `${ customer . first_name } - ${ customer . last_name } - bill-${ savedBill . date . toISOString ( ) } .html` ;
89135 const contentType = 'text/html' ;
90136 const bucket = process . env . S3_BUCKET_BILL ?? 'default-bill-bucket' ;
91- const fileUrl = await uploadFile (
92- fileName ,
93- Buffer . from ( billHtml ) ,
94- contentType ,
95- bucket ,
96- ) ;
137+ await uploadFile ( fileName , Buffer . from ( billHtml ) , contentType , bucket ) ;
138+ logger . info ( '☁️ Receipt uploaded to S3: %s' , fileName ) ;
97139
98- logger . info ( `📤 [SellService] Bill uploaded to S3: ${ fileUrl } ` ) ;
99-
100- // 📩 Email the bill
140+ // 6️⃣ Send the receipt via email to the customer
101141 await sendEmail (
102- [ savedCustomer ! . email ] ,
103- `Your Purchase Receipt - Bill #${ finalBill ! . id } ` ,
142+ [ customer . email ] ,
143+ `Your Purchase Receipt - Bill #${ savedBill . id } ` ,
104144 billHtml ,
105145 ) ;
146+ logger . info ( '📧 Receipt email sent to: %s' , customer . email ) ;
106147
107- logger . info (
108- `📧 [SellService] Bill sent to customer: ${ savedCustomer ! . email } ` ,
109- ) ;
110-
148+ // 7️⃣ Cache the saved bill
111149 await this . cache . set (
112- `bill:${ finalBill ! . id } ` ,
113- JSON . stringify ( finalBill ) ,
150+ `bill:${ savedBill . id } ` ,
151+ JSON . stringify ( savedBill ) ,
114152 3600 ,
115153 ) ;
154+ logger . info ( '🔒 Bill cached with key: bill:%s' , savedBill . id ) ;
116155
117- return finalBill ! ;
156+ logger . info ( '🎉 processSales completed successfully.' ) ;
157+ return true ;
118158 }
119159}
0 commit comments