11import logger from 'firebase-functions/logger' ;
22import api from '@cloudcommerce/api' ;
33import { newShipment , matchService , sortServicesBy } from './functions/new-shipment.mjs' ;
4- import meClient from './functions/client- melhor-envio.mjs' ;
4+ import { getMEAxios } from './util/ melhor-envio-api .mjs' ;
55import errorHandling from './functions/error-handling.mjs' ;
66
77export default async ( { params, application } ) => {
@@ -10,16 +10,18 @@ export default async ({ params, application }) => {
1010 ...application . hidden_data ,
1111 } ;
1212
13- const melhorEnvioToken = appData . access_token ;
14- if ( typeof melhorEnvioToken === 'string' && melhorEnvioToken ) {
15- process . env . MELHORENVIO_TOKEN = melhorEnvioToken ;
13+ if ( appData . access_token ) {
14+ process . env . MELHORENVIO_TOKEN = appData . access_token ;
1615 }
16+ process . env . MELHORENVIO_ENV = appData . sandbox
17+ ? 'sandbox'
18+ : process . env . MELHORENVIO_ENV || 'live' ;
1719 if ( ! process . env . MELHORENVIO_TOKEN ) {
1820 logger . warn ( 'Missing Melhor Envio token' ) ;
1921 return {
2022 status : 409 ,
2123 error : 'CALCULATE_SHIPPING_DISABLED' ,
22- message : 'Melhor Envio Token is unset ' ,
24+ message : 'Melhor Envio Token is not set ' ,
2325 } ;
2426 }
2527
@@ -79,57 +81,122 @@ export default async ({ params, application }) => {
7981 return response ;
8082 }
8183
84+ const meAxios = await getMEAxios ( ) ;
85+
8286 if ( params . items ) {
8387 const intZipCode = parseInt ( params . to . zip . replace ( / \D / g, '' ) , 10 ) ;
84- const { sandbox } = appData ;
85-
8688 if ( ! appData . merchant_address ) {
8789 // get merchant_address
88- const { data } = await meClient ( process . env . MELHORENVIO_TOKEN , sandbox ) . get ( '/' ) ;
90+ const { data } = await meAxios . get ( '/' ) ;
8991 const merchantAddress = data . address ;
9092
9193 // update config.merchant_address
9294 appData . merchant_address = merchantAddress ;
9395 // save merchant_address in hidden_data
9496
95- const bodyUpdate = {
96- merchant_address : merchantAddress ,
97- } ;
9897 try {
99- await api . patch ( `applications/${ application . _id } /hidden_data` , bodyUpdate ) ;
98+ await api . patch ( `applications/${ application . _id } /hidden_data` , {
99+ merchant_address : merchantAddress ,
100+ } ) ;
100101 } catch ( err ) {
101- logger . error ( '>>(App Melhor Envio): !<> Update merchant_address failed' , err . message ) ;
102+ logger . warn ( 'Update merchant_address failed' ) ;
103+ logger . error ( err ) ;
104+ }
105+ }
106+
107+ let originZip ;
108+ let warehouseCode ;
109+ let postingDeadline ;
110+ let warehouseConfig = { } ;
111+ let from = {
112+ zip : appData . merchant_address . postal_code ,
113+ street : appData . merchant_address . address ,
114+ number : parseInt ( appData . merchant_address . number , 10 ) ,
115+ } ;
116+ if ( params . from ) {
117+ from = params . from ;
118+ originZip = params . from . zip ;
119+ } else if ( Array . isArray ( appData . warehouses ) && appData . warehouses . length ) {
120+ for ( let i = 0 ; i < appData . warehouses . length ; i ++ ) {
121+ const warehouse = appData . warehouses [ i ] ;
122+ if ( warehouse && warehouse . zip && checkZipCode ( warehouse ) ) {
123+ const { code } = warehouse ;
124+ if ( ! code ) {
125+ continue ;
126+ }
127+ if (
128+ params . items
129+ && params . items . find ( ( { quantity, inventory } ) => {
130+ return inventory && Object . keys ( inventory ) . length
131+ && ! ( inventory [ code ] >= quantity ) ;
132+ } )
133+ ) {
134+ // item not available on current warehouse
135+ continue ;
136+ }
137+ originZip = warehouse . zip ;
138+ if ( warehouse . posting_deadline ) {
139+ postingDeadline = warehouse . posting_deadline ;
140+ }
141+ if ( warehouse && warehouse . street ) {
142+ [ 'zip' , 'street' , 'number' , 'complement' , 'borough' , 'city' , 'province_code' ]
143+ . forEach ( ( prop ) => {
144+ if ( warehouse [ prop ] ) {
145+ from [ prop ] = warehouse [ prop ] ;
146+ }
147+ } ) ;
148+ }
149+ /*
150+ if (warehouse.doc) {
151+ docNumber = warehouse.doc
152+ }
153+ */
154+ warehouseCode = code ;
155+ warehouseConfig = warehouse ;
156+ }
102157 }
103158 }
159+ if ( ! originZip ) {
160+ originZip = appData . merchant_address . postal_code ;
161+ }
162+ originZip = typeof originZip === 'string' ? originZip . replace ( / \D / g, '' ) : '' ;
104163
105164 let schema ;
106165 try {
107- schema = newShipment ( appData , params ) ;
166+ schema = newShipment ( appData , { originZip , ... params } ) ;
108167 } catch ( err ) {
109- logger . error ( '>>(App Melhor Envio): NEW_SHIPMENT_PARSE_ERR' , err ) ;
168+ logger . warn ( 'Failed parsing shipment' ) ;
169+ logger . error ( err ) ;
110170 return {
111171 status : 400 ,
112172 error : 'CALCULATE_ERR' ,
113173 message : 'Unexpected Error Try Later' ,
114174 } ;
115175 }
116176
117- // calculate the shipment
118177 try {
119- const data = await meClient ( process . env . MELHORENVIO_TOKEN , sandbox )
120- . post ( '/shipment/calculate' , schema ) ;
178+ const { data } = await meAxios . post ( '/shipment/calculate' , schema ) ;
179+
180+ if ( ! data . length ) {
181+ logger . warn ( 'Empty ME calculate result' , {
182+ data,
183+ schema,
184+ } ) ;
185+ }
121186
122187 let errorMsg ;
123188 data . forEach ( ( service ) => {
124189 let isAvailable = true ;
125190 // check if service is not disabled
126191 if ( Array . isArray ( appData . unavailable_for ) ) {
127192 for ( let i = 0 ; i < appData . unavailable_for . length ; i ++ ) {
128- if ( appData . unavailable_for [ i ] && appData . unavailable_for [ i ] . zip_range
193+ if (
194+ appData . unavailable_for [ i ] && appData . unavailable_for [ i ] . zip_range
129195 && appData . unavailable_for [ i ] . service_name
130196 ) {
131197 const unavailable = appData . unavailable_for [ i ] ;
132- if ( intZipCode >= unavailable . zip_range . min
198+ if (
199+ intZipCode >= unavailable . zip_range . min
133200 && intZipCode <= unavailable . zip_range . max
134201 && matchService ( unavailable , service . name )
135202 ) {
@@ -141,16 +208,14 @@ export default async ({ params, application }) => {
141208
142209 if ( ! service . error && isAvailable ) {
143210 // mounte response body
144- const { to } = params ;
145- const from = {
146- zip : appData . merchant_address . postal_code ,
147- street : appData . merchant_address . address ,
148- number : parseInt ( appData . merchant_address . number , 10 ) ,
149- } ;
150-
151211 const shippingLine = {
152- to,
153- from,
212+ from : {
213+ ...params . from ,
214+ ...appData . from ,
215+ ...from ,
216+ zip : originZip ,
217+ } ,
218+ to : params . to ,
154219 own_hand : service . additional_services . own_hand ,
155220 receipt : service . additional_services . receipt ,
156221 discount : 0 ,
@@ -162,7 +227,9 @@ export default async ({ params, application }) => {
162227 posting_deadline : {
163228 days : 3 ,
164229 ...appData . posting_deadline ,
230+ ...postingDeadline ,
165231 } ,
232+ warehouse_code : warehouseCode ,
166233 custom_fields : [
167234 {
168235 field : 'by_melhor_envio' ,
@@ -173,7 +240,6 @@ export default async ({ params, application }) => {
173240
174241 const servicePkg = ( service . packages && service . packages [ 0 ] )
175242 || ( service . volumes && service . volumes [ 0 ] ) ;
176-
177243 if ( servicePkg ) {
178244 shippingLine . package = { } ;
179245 if ( servicePkg . dimensions ) {
@@ -197,10 +263,11 @@ export default async ({ params, application }) => {
197263 }
198264 }
199265
200- if ( appData . jadlog_agency ) {
266+ const jadlogAgency = warehouseConfig . jadlog_agency || appData . jadlog_agency ;
267+ if ( jadlogAgency ) {
201268 shippingLine . custom_fields . push ( {
202269 field : 'jadlog_agency' ,
203- value : String ( appData . jadlog_agency ) ,
270+ value : String ( jadlogAgency ) ,
204271 } ) ;
205272 }
206273
@@ -218,14 +285,20 @@ export default async ({ params, application }) => {
218285 }
219286 // update total price
220287 shippingLine . total_price += appData . additional_price ;
288+ if ( shippingLine . total_price < 0 ) {
289+ shippingLine . total_price = 0 ;
290+ }
221291 }
222292
223- // search for discount by shipping rule
293+ // search for discount or fixed value by shipping rule
224294 if ( Array . isArray ( shippingRules ) ) {
225295 for ( let i = 0 ; i < shippingRules . length ; i ++ ) {
226296 const rule = shippingRules [ i ] ;
227- if ( rule && matchService ( rule , service . name )
228- && checkZipCode ( rule ) && ! ( rule . min_amount > params . subtotal )
297+ if (
298+ rule
299+ && matchService ( rule , service . name )
300+ && checkZipCode ( rule )
301+ && ! ( rule . min_amount > params . subtotal )
229302 ) {
230303 // valid shipping rule
231304 if ( rule . free_shipping ) {
@@ -237,24 +310,30 @@ export default async ({ params, application }) => {
237310 if ( rule . discount . percentage ) {
238311 discountValue *= ( shippingLine . total_price / 100 ) ;
239312 }
240- shippingLine . discount += discountValue ;
241- shippingLine . total_price -= discountValue ;
242- if ( shippingLine . total_price < 0 ) {
243- shippingLine . total_price = 0 ;
313+ if ( discountValue ) {
314+ shippingLine . discount += discountValue ;
315+ shippingLine . total_price -= discountValue ;
316+ if ( shippingLine . total_price < 0 ) {
317+ shippingLine . total_price = 0 ;
318+ }
244319 }
245320 break ;
321+ } else if ( rule . fixed ) {
322+ shippingLine . total_price = rule . fixed ;
246323 }
247324 }
248325 }
249326 }
250327
251- let label = service . name ;
252- if ( appData . services && Array . isArray ( appData . services ) && appData . services . length ) {
253- const serviceFound = appData . services . find ( ( lookForService ) => {
254- return lookForService && matchService ( lookForService , label ) ;
328+ let label = appData . prefix_labels === true
329+ ? `ME ${ service . name } `
330+ : service . name ;
331+ if ( Array . isArray ( appData . services ) && appData . services . length ) {
332+ const matchedService = appData . services . find ( ( _service ) => {
333+ return _service && matchService ( _service , label ) ;
255334 } ) ;
256- if ( serviceFound && serviceFound . label ) {
257- label = serviceFound . label ;
335+ if ( matchedService ? .label ) {
336+ label = matchedService . label ;
258337 }
259338 }
260339
@@ -271,21 +350,31 @@ export default async ({ params, application }) => {
271350 }
272351 } ) ;
273352
274- // sort services?
275- if ( appData . sort_services && Array . isArray ( response . shipping_services )
276- && response . shipping_services . length ) {
353+ if (
354+ appData . sort_services
355+ && Array . isArray ( response . shipping_services )
356+ && response . shipping_services . length
357+ ) {
277358 response . shipping_services . sort ( sortServicesBy ( appData . sort_services ) ) ;
278359 }
360+ if ( appData . max_services > 0 ) {
361+ response . shipping_services = response . shipping_services
362+ . slice ( 0 , appData . max_services ) ;
363+ }
279364
280- return ( ! Array . isArray ( response . shipping_services ) || ! response . shipping_services . length )
281- && errorMsg
282- ? {
283- status : 400 ,
284- error : 'CALCULATE_ERR_MSG' ,
285- message : errorMsg ,
365+ if (
366+ ! Array . isArray ( response . shipping_services )
367+ || ! response . shipping_services . length
368+ ) {
369+ if ( errorMsg ) {
370+ return {
371+ status : 400 ,
372+ error : 'CALCULATE_ERR_MSG' ,
373+ message : errorMsg ,
374+ } ;
286375 }
287- // success response with available shipping services
288- : response ;
376+ }
377+ return response ;
289378 } catch ( err ) {
290379 let message = 'Unexpected Error Try Later' ;
291380 if ( err . response && err . isAxiosError ) {
@@ -295,7 +384,11 @@ export default async ({ params, application }) => {
295384 message = 'Melhor Envio offline no momento' ;
296385 isAppError = false ;
297386 } else if ( data ) {
298- if ( data . errors && typeof data . errors === 'object' && Object . keys ( data . errors ) . length ) {
387+ if (
388+ data . errors
389+ && typeof data . errors === 'object'
390+ && Object . keys ( data . errors ) . length
391+ ) {
299392 const errorKeys = Object . keys ( data . errors ) ;
300393 for ( let i = 0 ; i < errorKeys . length ; i ++ ) {
301394 const meError = data . errors [ errorKeys [ i ] ] ;
@@ -320,27 +413,25 @@ export default async ({ params, application }) => {
320413
321414 if ( isAppError ) {
322415 // debug unexpected error
323- logger . error ( '>>(App Melhor Envio): CalculateShippingErr:' , JSON . stringify ( {
416+ logger . error ( 'CalculateShippingErr:' , {
324417 status,
325418 data,
326419 config,
327- } , null , 4 ) ) ;
420+ } ) ;
328421 }
329422 } else {
330423 errorHandling ( err ) ;
331424 }
332-
333425 return {
334426 status : 409 ,
335427 error : 'CALCULATE_ERR' ,
336428 message,
337429 } ;
338430 }
339- } else {
340- return {
341- status : 400 ,
342- error : 'CALCULATE_EMPTY_CART' ,
343- message : 'Cannot calculate shipping without cart items' ,
344- } ;
345431 }
432+ return {
433+ status : 400 ,
434+ error : 'CALCULATE_EMPTY_CART' ,
435+ message : 'Cannot calculate shipping without cart items' ,
436+ } ;
346437} ;
0 commit comments