import { date } from 'alga-js' import refreshTokenHelper from "../../../../utils/refreshTokenHelper" import getTokenHelper from "../../../../utils/getTokenHelper" import errorHandlingHelper from "../../../../utils/errorHandlingHelper" import fetchHelper from "../../../../utils/fetchHelper" import dhlHelper from "../../../../utils/dhlHelper" import dhlCustomHelper from "../../../../utils/dhlCustomHelper" import laravelHelper from "../../../../utils/laravelHelper" /** * Multi-Parcel DHL Label Creation Endpoint * * This endpoint creates multiple DHL labels for a single shipment (M_InOut) * where the items are split across multiple parcels based on weight limits. * * The first tracking number is saved to M_InOut.TrackingNo (for marketplace confirmation) * Additional tracking numbers are saved to M_InOut.trackingnoarray as JSON array */ const handleInoutFunc = async (event: any, authToken: any = null, option: any) => { let data: any = {} const token = authToken ?? await getTokenHelper(event) // Build tracking number array for additional parcels const trackingNoArray = option.all_tracking_numbers.slice(1) // All except the first one const trackingPayload: any = { TrackingNo: option.tracking_number, // First tracking number (for marketplace confirmation) DHL_Shipment_Code: option.shipment_code, DHL_Routing_Code: option.routing_code, DHL_Label_Base64: option.label_base64, IsCommissioned: true, shipping_date: date.now('', '', {timeZone: 'UTC'}).replace(' ', 'T')+'Z', shipping_service_name: option.shippingService, tableName: 'M_Inout', // Store additional tracking numbers in the new array field trackingnoarray: trackingNoArray.length > 0 ? JSON.stringify(trackingNoArray) : null } if(option.paket_type) { trackingPayload['M_Paket_Type_ID'] = option.paket_type } const resp: any = await fetchHelper(event, 'models/m_inout/'+option.inout_id, 'PUT', token, trackingPayload) if(resp) { data['shipment'] = resp data['status'] = 200 data['message'] = '' if(resp?.IsCommissionedConfirmed !== true) { const resp2: any = await fetchHelper(event, 'models/m_inout/'+option.inout_id+'?$expand=c_order_id,m_inoutline,c_bpartner_location_id,ad_org_id', 'GET', token, null) // Helper function to format order number with first word of company name const formatOrderNumber = () => { const documentNo = resp2?.C_Order_ID?.DocumentNo ?? '0' const companyName = resp2?.AD_Org_ID?.companyname ?? '' const firstWord = companyName.trim().split(/\s+/)[0] || '' return firstWord ? `${documentNo}-${firstWord}` : documentNo } if(resp2?.C_Order_ID?.C_OrderSource_ID?.id) { const resp3: any = await fetchHelper(event, 'models/c_ordersource/'+resp2.C_Order_ID.C_OrderSource_ID.id, 'GET', token, null) // Marketplace confirmations use only the FIRST tracking number // This is the same behavior as single-parcel mode if(resp2?.C_Order_ID?.shopware6_order_id) { if(resp3?.marketplace_url) { try { const resp4: any = await laravelHelper(event, 'sales/orders/mark-shopware-order-delivery', 'POST', { marketplace_url: resp3.marketplace_url, marketplace_key: resp3.marketplace_key, marketplace_secret: resp3.marketplace_secret, id: resp2.C_Order_ID.shopware6_order_id, trackingCodes: [option.tracking_number], // Only first tracking number mail: { email: option.customerEmail, isSentCustomTrackingMail: option.isSentCustomTrackingMail, orderNumber: formatOrderNumber(), name: resp2?.C_Order_ID?.C_BPartner?.identifier ?? 'Mr/Ms', company: 'LogYou GmbH', carrier: option.shippingService ?? 'DHL', lines: resp2?.m_inoutline?.map((i: any) => ({ description: i.M_Product_ID.identifier, quantity: i.QtyEntered })) ?? [], address1: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.Address1 ?? '', address2: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.Address2 ?? '', city: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.City ?? '', country: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.C_Country_ID?.identifier ?? '', postal: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.Postal ?? '' } }) if(resp4) { await fetchHelper(event, 'models/m_inout/'+option.inout_id, 'PUT', token, { IsCommissionedConfirmed: true, ack_commissioned_laravel: true, tableName: 'M_Inout' }) } } catch(err: any) { data['shopware'] = errorHandlingHelper(err?.data ?? err, err?.data ?? err) } } } if(resp2?.C_Order_ID?.shopify_order_id) { if(resp3?.marketplace_url) { try { const resp4: any = await laravelHelper(event, 'sales/orders/mark-shopify-order-delivery', 'POST', { orderSource: resp3, id: resp2.C_Order_ID.shopify_order_id, trackingCodes: { number: option.tracking_number, url: option.tracking_url, company: 'DHL Express' }, mail: { email: option.customerEmail, isSentCustomTrackingMail: option.isSentCustomTrackingMail, orderNumber: formatOrderNumber(), name: resp2?.C_Order_ID?.C_BPartner?.identifier ?? 'Mr/Ms', company: 'LogYou GmbH', carrier: option.shippingService ?? 'DHL', lines: resp2?.m_inoutline?.map((i: any) => ({ description: i.M_Product_ID.identifier, quantity: i.QtyEntered })) ?? [], address1: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.Address1 ?? '', address2: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.Address2 ?? '', city: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.City ?? '', country: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.C_Country_ID?.identifier ?? '', postal: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.Postal ?? '' } }) if(resp4) { await fetchHelper(event, 'models/m_inout/'+option.inout_id, 'PUT', token, { IsCommissionedConfirmed: true, ack_commissioned_laravel: true, tableName: 'M_Inout' }) } } catch(err: any) { data['shopify'] = errorHandlingHelper(err?.data ?? err, err?.data ?? err) } } } if(resp2?.C_Order_ID?.amazon_order_id) { if(resp3?.marketplace_url) { try { const resp4: any = await laravelHelper(event, 'sales/orders/mark-amazon-order-delivery', 'POST', { orderSource: resp3, id: resp2.C_Order_ID.amazon_order_id, details: { shippingDate: trackingPayload.shipping_date, carrierCode: 'DHL', shippingMethod: 'Paket', referenceId: resp2?.DocumentNo ?? option.inout_id }, trackingCodes: { number: option.tracking_number, url: option.tracking_url, }, mail: { email: option.customerEmail, isSentCustomTrackingMail: option.isSentCustomTrackingMail, orderNumber: formatOrderNumber(), name: resp2?.C_Order_ID?.C_BPartner?.identifier ?? 'Mr/Ms', company: 'LogYou GmbH', carrier: option.shippingService ?? 'DHL', lines: resp2?.m_inoutline?.map((i: any) => ({ description: i.M_Product_ID.identifier, quantity: i.QtyEntered })) ?? [], address1: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.Address1 ?? '', address2: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.Address2 ?? '', city: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.City ?? '', country: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.C_Country_ID?.identifier ?? '', postal: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.Postal ?? '' } }) if(resp4) { await fetchHelper(event, 'models/m_inout/'+option.inout_id, 'PUT', token, { IsCommissionedConfirmed: true, ack_commissioned_laravel: true, tableName: 'M_Inout' }) } } catch(err: any) { data['amazon'] = errorHandlingHelper(err?.data ?? err, err?.data ?? err) } } } if(resp2?.C_Order_ID?.plentyone_order_id) { if(resp3?.marketplace_url) { try { const plentyOneWeight = resp2?.m_inoutline?.reduce((acc: any, prev: any) => Number(acc['Weight'] ?? 0) + Number(prev['Weight'] ?? 0), 0) const resp4: any = await laravelHelper(event, 'sales/orders/mark-plentyone-order-delivery', 'POST', { orderSource: resp3, id: resp2.C_Order_ID.plentyone_order_id, details: { shippingDate: trackingPayload.shipping_date, carrierCode: 'DHL', shippingMethod: 'Paket', referenceId: resp2?.DocumentNo ?? option.inout_id, weight: plentyOneWeight ?? 0 }, trackingCodes: { number: option.tracking_number, url: option.tracking_url, }, mail: { email: option.customerEmail, isSentCustomTrackingMail: option.isSentCustomTrackingMail, orderNumber: formatOrderNumber(), name: resp2?.C_Order_ID?.C_BPartner?.identifier ?? 'Mr/Ms', company: 'LogYou GmbH', carrier: option.shippingService ?? 'DHL', lines: resp2?.m_inoutline?.map((i: any) => ({ description: i.M_Product_ID.identifier, quantity: i.QtyEntered })) ?? [], address1: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.Address1 ?? '', address2: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.Address2 ?? '', city: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.City ?? '', country: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.C_Country_ID?.identifier ?? '', postal: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.Postal ?? '' } }) if(resp4) { await fetchHelper(event, 'models/m_inout/'+option.inout_id, 'PUT', token, { IsCommissionedConfirmed: true, ack_commissioned_laravel: true, tableName: 'M_Inout' }) } } catch(err: any) { data['plentyone'] = errorHandlingHelper(err?.data ?? err, err?.data ?? err) } } } if(resp2?.C_Order_ID?.ExternalOrderId && resp3?.Marketplace?.identifier === 'jtl-ffn') { if(resp3?.marketplace_url) { try { const resp4: any = await laravelHelper(event, 'sales/orders/mark-jtl-order-delivery', 'POST', { orderSource: resp3, id: resp2.C_Order_ID.ExternalOrderId, details: { shippingDate: trackingPayload.shipping_date, carrierCode: 'DHL', shippingMethod: 'Paket', referenceId: resp2?.DocumentNo ?? option.inout_id, weight: 0 }, trackingCodes: { number: option.tracking_number, url: option.tracking_url, }, mail: { email: option.customerEmail, isSentCustomTrackingMail: option.isSentCustomTrackingMail, orderNumber: formatOrderNumber(), name: resp2?.C_Order_ID?.C_BPartner?.identifier ?? 'Mr/Ms', company: 'LogYou GmbH', carrier: option.shippingService ?? 'DHL', lines: resp2?.m_inoutline?.map((i: any) => ({ description: i.M_Product_ID.identifier, quantity: i.QtyEntered })) ?? [], address1: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.Address1 ?? '', address2: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.Address2 ?? '', city: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.City ?? '', country: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.C_Country_ID?.identifier ?? '', postal: resp2?.C_Order_ID?.C_BPartner_Location_ID?.C_Location_ID?.Postal ?? '' } }) if(resp4) { await fetchHelper(event, 'models/m_inout/'+option.inout_id, 'PUT', token, { IsCommissionedConfirmed: true, ack_commissioned_laravel: true, tableName: 'M_Inout' }) } } catch(err: any) { data['jtl'] = errorHandlingHelper(err?.data ?? err, err?.data ?? err) } } } } } } return data } const handleFunc = async (event: any) => { let data: any = {} const config = useRuntimeConfig() const body = await readBody(event) // Validate multi-parcel data if (!body.parcels || !Array.isArray(body.parcels) || body.parcels.length === 0) { return { status: 400, message: 'Multi-parcel mode requires parcels array with at least one parcel' } } let dhlCustomActive = false let dhlCustomData = {} let customerEmail = 'fulfillcustomer@logyou.de' let isSentCustomTrackingMail = false const tokenEmail = await getTokenHelper(event) const respInout: any = await fetchHelper(event, 'models/m_inout/'+body.inOutId+'?$expand=c_order_id,ad_org_id', 'GET', tokenEmail, null) if(respInout?.C_Order_ID?.C_OrderSource_ID?.id) { const respOrderSource: any = await fetchHelper(event, 'models/c_ordersource/'+respInout.C_Order_ID.C_OrderSource_ID.id, 'GET', tokenEmail, null) isSentCustomTrackingMail = respOrderSource?.isSentCustomTrackingMail ?? false if(body.email) { customerEmail = (respOrderSource?.isExcludetrackingmail ? 'fulfillcustomer@logyou.de' : body.email) } if(respOrderSource?.DHL_USE_CUSTOM == true && respOrderSource?.DHLURL) { dhlCustomActive = true dhlCustomData = { dhlurl: respOrderSource.DHLURL, dhlkey: respOrderSource.DHLKEY, dhlsecret: respOrderSource.DHLSECRET, dhluser: respOrderSource.DHLUSER, dhlpass: respOrderSource.DHLPASS, iscustom: respOrderSource.DHL_USE_CUSTOM, dhl_billing_number_de: respOrderSource.dhl_billing_number_de || '63291441520101', dhl_billing_number_int: respOrderSource.dhl_billing_number_int || '63291441525301' } } } let customerPhone = '+49 987654321' if(body.telephone) { customerPhone = body.telephone } // Format order number with first word of company name const companyName = respInout?.AD_Org_ID?.companyname ?? '' const firstWord = companyName.trim().split(/\s+/)[0]?.replace(/[^a-zA-Z0-9]/g, '') || '' const baseOrderNumber = body.orderNumber ?? body.inOutUId.replaceAll('-', '') const formattedOrderNumber = firstWord ? `${baseOrderNumber}-${firstWord}` : baseOrderNumber let refNo = formattedOrderNumber if(refNo.length < 9) { let refNoDigit = '0' for(let i = 0; i < (9 - Number(refNo.length)); i++) { refNoDigit += '0' refNo = `${refNoDigit}${refNo}` } } // Consignee info (same for all parcels) let newConsigneeValue = {} if(body.name2) { newConsigneeValue = {...newConsigneeValue, name2: String(body.name2 ?? '').trim()} } if(body.address2) { newConsigneeValue = {...newConsigneeValue, name3: String(body.address2 ?? '').trim()} } // Build shipments array for DHL API - one shipment per parcel slot const shipments: any[] = [] for (let i = 0; i < body.parcels.length; i++) { const parcel = body.parcels[i] const parcelRefNo = `${refNo}-P${i + 1}` // Add parcel suffix to reference // Build customs object for this parcel let customsObj: any = { exportType: 'OTHER', postalCharges: { currency: body.totalOrderValueCurrency ?? 'EUR', value: 0 }, } if(parcel.items && parcel.items.length > 0) { customsObj.items = parcel.items.map((item: any) => ({ itemDescription: String(item.description ?? '').trim().substring(0, 256), packagedQuantity: item.quantity, itemValue: { currency: body.totalOrderValueCurrency ?? 'EUR', value: item.value }, itemWeight: { uom: 'kg', value: item.weight }, })) } if(body.orderNumber) { customsObj.invoiceNo = String(body.orderNumber).substring(0, 35) } const payLoad = { product: body.country === 'DE' ? 'V01PAK' : 'V53WPAK', billingNumber: body.country === 'DE' ? '63807218920101' : '63807218925301', refNo: parcelRefNo, shipper: { name1: respInout?.AD_Org_ID?.companyname && respInout?.AD_Org_ID !== 1000000 ? respInout?.AD_Org_ID?.companyname : 'LogYou GmbH', name2: respInout?.AD_Org_ID?.companyname && respInout?.AD_Org_ID !== 1000000 ? 'c/o LogYou GmbH' : '', addressStreet: 'Mühlenweg', addressHouse: '4', postalCode: '35510', city: 'Butzbach', country: 'DEU', email: 'info@logyou.de', phone: '+4960339160570' }, consignee: { name1: String(body.name ?? '').trim(), addressStreet: String(body.address ?? '').trim(), addressHouse: String(body.houseNumber || 0).trim(), postalCode: String(body.postalCode ?? '').trim(), city: String(body.city ?? '').trim(), country: body.countryIso3 || body.countryName?.toUpperCase()?.replaceAll(' ', '')?.slice(0, 3), email: String(customerEmail ?? '').trim(), phone: String(customerPhone ?? '').trim(), ...newConsigneeValue }, details: { dim: { uom: 'cm', height: parseFloat(Number(body.height ?? 0).toFixed(2)), length: parseFloat(Number(body.length ?? 0).toFixed(2)), width: parseFloat(Number(body.width ?? 0).toFixed(2)) }, weight: { uom: 'kg', // Use the parcel's specific weight + 0.25kg packaging value: parseFloat((Number(parcel.weight ?? 0) + 0.25).toFixed(2)) } }, customs: customsObj } // Override billing number if using custom DHL if(dhlCustomActive) { payLoad['billingNumber'] = body.country === 'DE' ? dhlCustomData['dhl_billing_number_de'] : dhlCustomData['dhl_billing_number_int'] } shipments.push(payLoad) } // Call DHL API with all shipments in one request let res: any = {} if(dhlCustomActive) { res = await dhlCustomHelper(event, dhlCustomData, 'shipping/v2/orders?printFormat=910-300-400', 'POST', { profile: 'STANDARD_GRUPPENPROFIL', shipments: shipments }) } else { res = await dhlHelper(event, 'shipping/v2/orders?printFormat=910-300-400', 'POST', { profile: 'STANDARD_GRUPPENPROFIL', shipments: shipments }) } // Process response - extract all tracking numbers and labels if(res?.items && res.items.length > 0) { const allTrackingNumbers: string[] = [] const allLabels: any[] = [] for (const item of res.items) { if (item.shipmentNo) { allTrackingNumbers.push(item.shipmentNo) allLabels.push({ shipmentNo: item.shipmentNo, routingCode: item.routingCode, label: item.label }) } } data['dhl_parcels'] = allLabels data['tracking_numbers'] = allTrackingNumbers data['parcel_count'] = allTrackingNumbers.length // Save to M_InOut - first tracking number goes to TrackingNo, rest to trackingnoarray if (allTrackingNumbers.length > 0) { try { const firstLabel = allLabels[0] const res2 = await handleInoutFunc(event, null, { inout_id: body.inOutId, tracking_number: allTrackingNumbers[0], // First tracking number for marketplace tracking_url: firstLabel.routingCode, shipment_code: allTrackingNumbers.join(','), // All tracking numbers comma-separated routing_code: firstLabel.routingCode, label_base64: firstLabel.label?.b64 || '', paket_type: body.paketType, shippingService: body.shippingService, customerEmail: customerEmail, isSentCustomTrackingMail: isSentCustomTrackingMail, all_tracking_numbers: allTrackingNumbers // For trackingnoarray field }) data = {...data, ...res2} } catch(err: any) { try { let authToken: any = await refreshTokenHelper(event) const firstLabel = allLabels[0] const res3 = await handleInoutFunc(event, authToken, { inout_id: body.inOutId, tracking_number: allTrackingNumbers[0], tracking_url: firstLabel.routingCode, shipment_code: allTrackingNumbers.join(','), routing_code: firstLabel.routingCode, label_base64: firstLabel.label?.b64 || '', paket_type: body.paketType, shippingService: body.shippingService, customerEmail: customerEmail, isSentCustomTrackingMail: isSentCustomTrackingMail, all_tracking_numbers: allTrackingNumbers }) data = {...data, ...res3} } catch(error: any) { data = errorHandlingHelper(err?.data ?? err, error?.data ?? error) } } } } else { data['status'] = 500 data['message'] = res?.status?.detail ?? 'DHL Multi-Parcel labels could not be created!' } return data } export default defineEventHandler(async (event) => { let data: any = {} try { data = await handleFunc(event) } catch(err: any) { data = errorHandlingHelper(err?.data ?? err, err?.data ?? err) } return data })