Skip to content

Commit 884a2ad

Browse files
[IMP] delivery_postlogistics: refactor API of web service class to avoid loss when several unchained modules implements new behaviors in it
1 parent 1f3da3e commit 884a2ad

File tree

5 files changed

+338
-235
lines changed

5 files changed

+338
-235
lines changed
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[checklog-odoo]
2+
ignore=
3+
WARNING.*DeprecationWarning

delivery_postlogistics/models/stock_picking.py

+222-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
# Copyright 2013 Camptocamp SA
22
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
33
import base64
4+
import logging
5+
from io import BytesIO
46
from operator import attrgetter
57

68
import lxml.html
9+
from PIL import Image
710

811
from odoo import api, fields, models, tools
912
from odoo.exceptions import UserError
1013

11-
from ..postlogistics.web_service import PostlogisticsWebService
14+
from ..postlogistics.web_service import PostlogisticsWebService, sanitize_string
15+
16+
_logger = logging.getLogger(__name__)
1217

1318

1419
class StockPicking(models.Model):
@@ -27,6 +32,16 @@ class StockPicking(models.Model):
2732
"Mobile", help="For notify delivery by telephone (ZAW3213)"
2833
)
2934

35+
def _get_packages_from_picking(self):
36+
# As CI doesn't allow warnings, we need to log as info here
37+
# (deprecated decorator is unhelpfull).
38+
_logger.info(
39+
"This method will be removed in version 18.0. \
40+
Please use _get_quant_packages_from_picking instead."
41+
)
42+
# TODO: remove this method in version > 18.0
43+
return self._get_quant_packages_from_picking()
44+
3045
def _get_quant_packages_from_picking(self):
3146
"""Get all the quant packages from the picking"""
3247
self.ensure_one()
@@ -235,3 +250,209 @@ def action_generate_carrier_label(self):
235250
if not self.carrier_id:
236251
raise UserError(self.env._("Please, set a carrier."))
237252
self.env["delivery.carrier"].postlogistics_send_shipping(self)
253+
254+
#
255+
# Postlogistics specific methods allowing proper override
256+
#
257+
258+
def get_package_number_hook(self, package):
259+
"""Hook method to customize the package number retrieval"""
260+
return None
261+
262+
def get_recipient_partner_hook(self):
263+
"""Hook method to customize the partner retrieval"""
264+
self.ensure_one()
265+
if self.picking_type_id.code != "outgoing":
266+
return (
267+
self.location_dest_id.company_id.partner_id
268+
or self.env.user.company_id.partner_id
269+
)
270+
return self.partner_id
271+
272+
def postlogistics_label_prepare_attributes(
273+
self, pack=None, pack_num=None, pack_total=None, pack_weight=None
274+
):
275+
"""This method aims to prepare a dictionary of attributes to be sent
276+
to the PostLogistics API"""
277+
self.ensure_one()
278+
package_type = (
279+
pack
280+
and pack.package_type_id
281+
or self.carrier_id.postlogistics_default_package_type_id
282+
)
283+
package_codes = package_type._get_shipper_package_code_list()
284+
285+
if pack_weight:
286+
total_weight = pack_weight
287+
else:
288+
total_weight = pack.shipping_weight if pack else self.shipping_weight
289+
total_weight *= 1000
290+
291+
if not package_codes:
292+
raise UserError(
293+
self.env._(
294+
"No PostLogistics packaging services found "
295+
"in package type {package_type_name}, for picking {picking_name}."
296+
).format(package_type_name=package_type.name, picking_name=self.name)
297+
)
298+
299+
# Activate phone notification ZAW3213
300+
# if phone call notification is set on partner
301+
if self.partner_id.postlogistics_notification == "phone":
302+
package_codes.append("ZAW3213")
303+
304+
attributes = {
305+
"weight": int(total_weight),
306+
}
307+
308+
# Remove the services if the delivery fixed date is not set
309+
if "ZAW3217" in package_codes:
310+
if self.delivery_fixed_date:
311+
attributes["deliveryDate"] = self.delivery_fixed_date
312+
else:
313+
package_codes.remove("ZAW3217")
314+
315+
# parcelNo / parcelTotal cannot be used if service ZAW3218 is not activated
316+
if "ZAW3218" in package_codes:
317+
if pack_total > 1:
318+
attributes.update(
319+
{"parcelTotal": pack_total - 1, "parcelNo": pack_num - 1}
320+
)
321+
else:
322+
package_codes.remove("ZAW3218")
323+
324+
if "ZAW3219" in package_codes and self.delivery_place:
325+
attributes["deliveryPlace"] = self.delivery_place
326+
if self.carrier_id.postlogistics_proclima_logo:
327+
attributes["proClima"] = True
328+
else:
329+
attributes["proClima"] = False
330+
331+
attributes["przl"] = package_codes
332+
333+
return attributes
334+
335+
def postlogistics_label_prepare_customer(self):
336+
"""Create a ns0:Customer as a dict from picking
337+
338+
This is the PostLogistics Customer, thus the sender
339+
340+
:param picking: picking browse record
341+
:return a dict containing data for ns0:Customer
342+
343+
"""
344+
self.ensure_one()
345+
company = self.company_id
346+
partner = company.partner_id
347+
if self.picking_type_id.code != "outgoing":
348+
partner = self.partner_id
349+
350+
partner_name = partner.name or partner.parent_id.name
351+
if not partner_name:
352+
raise UserError(self.env._("Customer name is required."))
353+
customer = {
354+
"name1": sanitize_string(partner_name)[:25],
355+
"street": sanitize_string(partner.street)[:25],
356+
"zip": sanitize_string(partner.zip)[:10],
357+
"city": sanitize_string(partner.city)[:25],
358+
"country": partner.country_id.code,
359+
"domicilePostOffice": self.carrier_id.postlogistics_office or None,
360+
}
361+
logo = self.carrier_id.postlogistics_logo
362+
if logo:
363+
logo_image = Image.open(BytesIO(base64.b64decode(logo)))
364+
logo_format = logo_image.format
365+
customer["logo"] = logo.decode()
366+
customer["logoFormat"] = logo_format
367+
return customer
368+
369+
def postlogistics_label_prepare_recipient(self, sanitize_mapping=None):
370+
"""Create a ns0:Recipient as a dict from a partner
371+
372+
:param partner: partner browse record
373+
:return a dict containing data for ns0:Recipient
374+
375+
"""
376+
partner = self.get_recipient_partner_hook()
377+
378+
partner_mobile = sanitize_string(
379+
self.delivery_mobile or partner.mobile, sanitize_mapping
380+
)
381+
partner_phone = sanitize_string(
382+
self.delivery_phone or partner.phone, sanitize_mapping
383+
)
384+
385+
if partner.postlogistics_notification == "email" and not partner.email:
386+
raise UserError(self.env._("Email is required for notification."))
387+
elif partner.postlogistics_notification == "sms" and not partner_mobile:
388+
raise UserError(
389+
self.env._("Mobile number is required for sms notification.")
390+
)
391+
elif partner.postlogistics_notification == "phone" and not partner_phone:
392+
raise UserError(
393+
self.env._("Phone number is required for phone call notification.")
394+
)
395+
396+
if not partner.street:
397+
raise UserError(self.env._("Partner street is required."))
398+
399+
if not partner.name and not partner.parent_id.name:
400+
raise UserError(self.env._("Partner name is required."))
401+
402+
if not partner.zip:
403+
raise UserError(self.env._("Partner zip is required."))
404+
405+
if not partner.city:
406+
raise UserError(self.env._("Partner city is required."))
407+
408+
partner_name = partner.name or partner.parent_id.name
409+
sanitized_partner_name = sanitize_string(partner_name, sanitize_mapping)
410+
partner_street = sanitize_string(partner.street, sanitize_mapping)
411+
partner_zip = sanitize_string(partner.zip, sanitize_mapping)
412+
partner_city = sanitize_string(partner.city, sanitize_mapping)
413+
recipient = {
414+
"name1": sanitized_partner_name[:35],
415+
"street": partner_street[:35],
416+
"zip": partner_zip[:10],
417+
"city": partner_city[:35],
418+
}
419+
420+
if partner.country_id.code:
421+
country_code = sanitize_string(
422+
partner.country_id.code.upper(), sanitize_mapping
423+
)
424+
recipient["country"] = country_code
425+
426+
if partner.street2:
427+
# addressSuffix is shown before street on label
428+
recipient["addressSuffix"] = recipient["street"]
429+
recipient["street"] = sanitize_string(
430+
partner.street2[:35], sanitize_mapping
431+
)
432+
433+
company_partner_name = partner.commercial_company_name
434+
if company_partner_name and company_partner_name != partner_name:
435+
parent_name = sanitize_string(partner.parent_id.name, sanitize_mapping)
436+
recipient["name2"] = parent_name[:35]
437+
recipient["personallyAddressed"] = False
438+
439+
# Phone and / or mobile should only be displayed if instruction to
440+
# Notify delivery by telephone is set
441+
if partner.postlogistics_notification == "email":
442+
recipient["email"] = sanitize_string(partner.email, sanitize_mapping)
443+
elif partner.postlogistics_notification == "phone":
444+
recipient["phone"] = sanitize_string(partner_phone, sanitize_mapping)
445+
if partner_mobile:
446+
recipient["mobile"] = partner_mobile
447+
elif partner.postlogistics_notification == "sms":
448+
recipient["mobile"] = partner_mobile
449+
450+
return recipient
451+
452+
def postlogistics_label_cash_on_delivery(self, package=None):
453+
amount = (package or self).postlogistics_cod_amount()
454+
amount = f"{amount:.2f}"
455+
return [{"Type": "NN_BETRAG", "Value": amount}]
456+
457+
def postlogistics_label_get_item_additional_data(self, package=None):
458+
return []

0 commit comments

Comments
 (0)