Download ops/backend-dm/controllers/ewayBillController.js from MouleeswaranM/FairRelay: direct link, hf CLI and curl.
- Browser
- Download file 12 kB
-
https://huggingface.co/MouleeswaranM/FairRelay/resolve/refs%2Fpr%2F21/ops/backend-dm/controllers/ewayBillController.js
- Command line
-
hf download hf://MouleeswaranM/FairRelay@refs/pr/21/ops/backend-dm/controllers/ewayBillController.js
-
curl -L -o ewayBillController.js https://huggingface.co/MouleeswaranM/FairRelay/resolve/refs%2Fpr%2F21/ops/backend-dm/controllers/ewayBillController.js
12 kB
| const prisma = require('../config/database'); | |
| const EWayBillService = require('../services/ewayBill.service'); | |
| const { v4: uuidv4 } = require('uuid'); | |
| /** | |
| * Generate a unique 12-digit numeric e-Way Bill number | |
| */ | |
| const generateBillNo = () => { | |
| return Math.floor(100000000000 + Math.random() * 900000000000).toString(); | |
| }; | |
| /** | |
| * Calculate validity days based on distance (1 day per 100km per government rules for regular cargo) | |
| */ | |
| const calculateValidity = (distanceKm) => { | |
| const days = Math.max(1, Math.ceil(distanceKm / 100)); // Refined rule: 100km per day | |
| const validUntil = new Date(); | |
| validUntil.setDate(validUntil.getDate() + days); | |
| return validUntil; | |
| }; | |
| /** | |
| * Resolve GSTIN for Recipient by traversing Delivery -> Shipment -> Customer | |
| */ | |
| const resolveRecipientGstin = async (delivery, tx) => { | |
| if (delivery.shipment && delivery.shipment.recipient && delivery.shipment.recipient.gstin) { | |
| return delivery.shipment.recipient.gstin; | |
| } | |
| // Fallback or detailed lookup | |
| const fullDelivery = await tx.delivery.findUnique({ | |
| where: { id: delivery.id }, | |
| include: { | |
| shipment: { | |
| include: { | |
| recipient: true | |
| } | |
| } | |
| } | |
| }); | |
| return fullDelivery?.shipment?.recipient?.gstin || '27BBBBB0000B1Z5'; | |
| }; | |
| /** | |
| * POST /api/eway-bill/absorption | |
| * Refined Handover Logic with strict identification | |
| */ | |
| const handleAbsorption = async (req, res) => { | |
| const { | |
| exporterPhone, | |
| importerPhone, | |
| exporterTruckPlate, | |
| importerTruckPlate, | |
| exchangedGoods | |
| } = req.body; | |
| const dispatcherId = req.user?.id; // From JWT middleware | |
| try { | |
| // 1. Resolve & Update Data using Prisma Transaction | |
| const transactionResult = await prisma.$transaction(async (tx) => { | |
| // Find Dispatcher and their Company | |
| const dispatcher = await tx.user.findUnique({ | |
| where: { id: dispatcherId || 'mock-dispatcher-id' }, // Handle missing for testing | |
| include: { courierCompany: true } | |
| }); | |
| // Find Drivers by Phone | |
| const exporterDriver = await tx.user.findUnique({ | |
| where: { phone: exporterPhone }, | |
| include: { courierCompany: true } | |
| }); | |
| const importerDriver = await tx.user.findUnique({ | |
| where: { phone: importerPhone }, | |
| include: { courierCompany: true } | |
| }); | |
| if (!exporterDriver || !importerDriver) { | |
| throw new Error('One or both drivers not found by phone number'); | |
| } | |
| // Find Trucks by License Plate | |
| const truckA = await tx.truck.findUnique({ | |
| where: { licensePlate: exporterTruckPlate } | |
| }); | |
| const truckB = await tx.truck.findUnique({ | |
| where: { licensePlate: importerTruckPlate } | |
| }); | |
| if (!truckA || !truckB) { | |
| throw new Error('One or both trucks not found by license plate'); | |
| } | |
| // Fetch Exporter's Deliveries to separate remaining from transferred | |
| const exporterDeliveries = await tx.delivery.findMany({ | |
| where: { | |
| driverId: exporterDriver.id, | |
| status: { not: 'COMPLETED' } | |
| }, | |
| include: { | |
| shipment: { | |
| include: { recipient: true } | |
| } | |
| } | |
| }); | |
| const transferredDeliveries = exporterDeliveries.filter(d => exchangedGoods.includes(d.id)); | |
| const remainingDeliveries = exporterDeliveries.filter(d => !exchangedGoods.includes(d.id)); | |
| if (transferredDeliveries.length === 0) { | |
| throw new Error('No valid deliveries found to transfer'); | |
| } | |
| // Update Deliveries: Transfer to Truck B / Importer | |
| await tx.delivery.updateMany({ | |
| where: { id: { in: exchangedGoods } }, | |
| data: { | |
| driverId: importerDriver.id, | |
| truckId: truckB.id, | |
| status: 'ABSORPTION_TRANSFERRED' | |
| } | |
| }); | |
| // Resolve GSTINs | |
| const supplierGstin = dispatcher?.courierCompany?.gstin || exporterDriver.courierCompany?.gstin || '27AAAAA0000A1Z5'; | |
| // Shared Validity/Date Info | |
| const now = new Date(); | |
| const genDateStr = now.toLocaleString(); | |
| const docDateStr = now.toLocaleDateString(); | |
| // --- Generate Bill A Data (Exporter - Remaining) --- | |
| const billANo = generateBillNo(); | |
| const totalDistA = remainingDeliveries.reduce((sum, d) => sum + (d.distanceKm || 0), 0); | |
| const validUntilA = calculateValidity(totalDistA); | |
| const recipientGstinA = remainingDeliveries.length > 0 ? await resolveRecipientGstin(remainingDeliveries[0], tx) : '27BBBBB0000B1Z5'; | |
| const qrDataA = await EWayBillService.generateQRCode({ | |
| billNo: billANo, | |
| driverName: exporterDriver.name, | |
| vehicleNo: truckA.licensePlate | |
| }); | |
| const billAData = { | |
| billNo: billANo, | |
| generatedDate: genDateStr, | |
| generatedBy: exporterDriver.name, | |
| validFrom: genDateStr, | |
| validUntil: validUntilA.toLocaleString(), | |
| qrCode: qrDataA, | |
| supplierGstin, | |
| dispatchPlace: exporterDriver.homeBaseCity || 'Origin Hub', | |
| recipientGstin: recipientGstinA, | |
| deliveryPlace: remainingDeliveries[0]?.dropLocation || 'Transit Point', | |
| docNo: `E-ABS-${uuidv4().substring(0, 8).toUpperCase()}`, | |
| docDate: docDateStr, | |
| transactionType: 'Absorption - Remaining', | |
| vehicleNo: truckA.licensePlate, | |
| fromLocation: exporterDriver.homeBaseCity || 'Current Location', | |
| goods: remainingDeliveries.map(d => ({ | |
| hsnCode: '8708', | |
| productName: d.cargoType, | |
| quantity: d.packageCount, | |
| unit: 'NOS', | |
| value: d.baseEarnings.toFixed(2) | |
| })) | |
| }; | |
| // --- Generate Bill B Data (Importer - Original + Absorbed) --- | |
| const importerExistingDeliveries = await tx.delivery.findMany({ | |
| where: { | |
| driverId: importerDriver.id, | |
| status: { not: 'COMPLETED' } | |
| }, | |
| include: { | |
| shipment: { | |
| include: { recipient: true } | |
| } | |
| } | |
| }); | |
| const combinedDeliveriesB = [...importerExistingDeliveries, ...transferredDeliveries]; | |
| const billBNo = generateBillNo(); | |
| const totalDistB = combinedDeliveriesB.reduce((sum, d) => sum + (d.distanceKm || 0), 0); | |
| const validUntilB = calculateValidity(totalDistB); | |
| const recipientGstinB = combinedDeliveriesB.length > 0 ? await resolveRecipientGstin(combinedDeliveriesB[0], tx) : '27CCCCC0000C1Z5'; | |
| const qrDataB = await EWayBillService.generateQRCode({ | |
| billNo: billBNo, | |
| driverName: importerDriver.name, | |
| vehicleNo: truckB.licensePlate | |
| }); | |
| const billBData = { | |
| billNo: billBNo, | |
| generatedDate: genDateStr, | |
| generatedBy: importerDriver.name, | |
| validFrom: genDateStr, | |
| validUntil: validUntilB.toLocaleString(), | |
| qrCode: qrDataB, | |
| supplierGstin, | |
| dispatchPlace: importerDriver.homeBaseCity || 'Transit Hub', | |
| recipientGstin: recipientGstinB, | |
| deliveryPlace: combinedDeliveriesB[0]?.dropLocation || 'Final Hub', | |
| docNo: `I-ABS-${uuidv4().substring(0, 8).toUpperCase()}`, | |
| docDate: docDateStr, | |
| transactionType: 'Absorption - Combined', | |
| vehicleNo: truckB.licensePlate, | |
| fromLocation: importerDriver.homeBaseCity || 'Handover Point', | |
| goods: combinedDeliveriesB.map(d => ({ | |
| hsnCode: '8708', | |
| productName: d.cargoType, | |
| quantity: d.packageCount, | |
| unit: 'NOS', | |
| value: d.baseEarnings.toFixed(2) | |
| })) | |
| }; | |
| // Create EWayBill records in DB | |
| await tx.eWayBill.createMany({ | |
| data: [ | |
| { | |
| billNo: billANo, | |
| vehicleNo: truckA.licensePlate, | |
| from: billAData.fromLocation, | |
| to: billAData.deliveryPlace, | |
| distance: totalDistA.toString(), | |
| driverId: exporterDriver.id, | |
| cargoValue: remainingDeliveries.reduce((sum, d) => sum + d.baseEarnings, 0).toString(), | |
| validUntil: validUntilA, | |
| status: 'ACTIVE' | |
| }, | |
| { | |
| billNo: billBNo, | |
| vehicleNo: truckB.licensePlate, | |
| from: billBData.fromLocation, | |
| to: billBData.deliveryPlace, | |
| distance: totalDistB.toString(), | |
| driverId: importerDriver.id, | |
| cargoValue: combinedDeliveriesB.reduce((sum, d) => sum + d.baseEarnings, 0).toString(), | |
| validUntil: validUntilB, | |
| status: 'ACTIVE' | |
| } | |
| ] | |
| }); | |
| // Update existing AbsorptionTransfer record if it exists | |
| await tx.absorptionTransfer.updateMany({ | |
| where: { | |
| exporterDriverId: exporterDriver.id, | |
| importerDriverId: importerDriver.id, | |
| status: 'PENDING' | |
| }, | |
| data: { | |
| status: 'COMPLETED', | |
| completedAt: now | |
| } | |
| }); | |
| return { billAData, billBData }; | |
| }); | |
| // 2. Render PDFs (Outside transaction for performance) | |
| console.log('Generating PDFs for:', { | |
| billA: transactionResult.billAData.billNo, | |
| billB: transactionResult.billBData.billNo | |
| }); | |
| const [pdfA, pdfB] = await Promise.all([ | |
| EWayBillService.generatePDF(transactionResult.billAData), | |
| EWayBillService.generatePDF(transactionResult.billBData) | |
| ]); | |
| console.log('PDF Generation complete. Result types:', { | |
| pdfA: typeof pdfA, | |
| pdfB: typeof pdfB, | |
| isBufferA: Buffer.isBuffer(pdfA), | |
| isBufferB: Buffer.isBuffer(pdfB) | |
| }); | |
| if (!pdfA || !pdfB) { | |
| throw new Error(`PDF generation returned empty: pdfA=${!!pdfA}, pdfB=${!!pdfB}`); | |
| } | |
| // 3. Zip and stream | |
| const zipBuffer = await EWayBillService.createZip([ | |
| { name: `Truck_A_EWayBill_${transactionResult.billAData.billNo}.pdf`, buffer: pdfA }, | |
| { name: `Truck_B_EWayBill_${transactionResult.billBData.billNo}.pdf`, buffer: pdfB } | |
| ]); | |
| res.setHeader('Content-Type', 'application/zip'); | |
| res.setHeader('Content-Disposition', 'attachment; filename=ewaybills_absorption.zip'); | |
| res.status(200).send(zipBuffer); | |
| } catch (error) { | |
| console.error('Absorption Handover Error:', error); | |
| res.status(500).json({ | |
| success: false, | |
| message: error.message || 'Internal Server Error' | |
| }); | |
| } | |
| }; | |
| module.exports = { | |
| handleAbsorption | |
| }; | |