From 4d54c0b326d9113912295b3d611c73c342402e82 Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Wed, 26 May 2021 20:06:58 +0200 Subject: [PATCH 01/13] Precommit and rename delivery_roulier_geodis on delivery_roulier_geodis_fr --- delivery_roulier_geodis_fr/README.rst | 47 ++++ delivery_roulier_geodis_fr/__init__.py | 1 + delivery_roulier_geodis_fr/__manifest__.py | 29 +++ delivery_roulier_geodis_fr/data/delivery.xml | 164 ++++++++++++ delivery_roulier_geodis_fr/data/keychain.xml | 13 + .../data/sequence_geodis.xml | 9 + delivery_roulier_geodis_fr/models/__init__.py | 5 + delivery_roulier_geodis_fr/models/delivery.py | 18 ++ delivery_roulier_geodis_fr/models/deposit.py | 173 +++++++++++++ delivery_roulier_geodis_fr/models/keychain.py | 44 ++++ .../models/stock_picking.py | 242 ++++++++++++++++++ .../models/stock_quant_package.py | 64 +++++ .../static/src/img/icon.png | Bin 0 -> 3338 bytes .../static/src/img/logo.jpg | Bin 0 -> 3895 bytes 14 files changed, 809 insertions(+) create mode 100644 delivery_roulier_geodis_fr/README.rst create mode 100644 delivery_roulier_geodis_fr/__init__.py create mode 100644 delivery_roulier_geodis_fr/__manifest__.py create mode 100644 delivery_roulier_geodis_fr/data/delivery.xml create mode 100644 delivery_roulier_geodis_fr/data/keychain.xml create mode 100644 delivery_roulier_geodis_fr/data/sequence_geodis.xml create mode 100644 delivery_roulier_geodis_fr/models/__init__.py create mode 100644 delivery_roulier_geodis_fr/models/delivery.py create mode 100644 delivery_roulier_geodis_fr/models/deposit.py create mode 100644 delivery_roulier_geodis_fr/models/keychain.py create mode 100644 delivery_roulier_geodis_fr/models/stock_picking.py create mode 100644 delivery_roulier_geodis_fr/models/stock_quant_package.py create mode 100644 delivery_roulier_geodis_fr/static/src/img/icon.png create mode 100644 delivery_roulier_geodis_fr/static/src/img/logo.jpg diff --git a/delivery_roulier_geodis_fr/README.rst b/delivery_roulier_geodis_fr/README.rst new file mode 100644 index 0000000000..90053519f7 --- /dev/null +++ b/delivery_roulier_geodis_fr/README.rst @@ -0,0 +1,47 @@ +Delivery Carrier Geodis +======================= + + +Description +----------- +Send parcels with Geodis. +Labels are generated from WebServices. +Edi file is generated locally and should be sent +by another module. + +Glossary +-------- + +Agency: Geodis's hub your warehouse depends upon. + +Configuration + +## Create a partner for your agency. + +This modules comes with only one partner "Geodis". It's the head quarters of Geodis. +You need to create partners for the agency you depends : +- create a sub contact of "Geodis HQ", +- pay attention to fill correctly name, streets, phone, zip code, country and *SIRET* +- fill "ref" (internal reference) field with the agency id. + + +Features: +- Multiple Agencies. + +Known Issues: +~~~~~~~~~~~~~ + +- each pack is sent on his own : no handling of numbers of picking + + +Technical references +-------------------- + +'Geodis documentation: www.geodis.fr' + +Contributors +------------ + +* Raphaël REVERDY +* Eric Bouhana + diff --git a/delivery_roulier_geodis_fr/__init__.py b/delivery_roulier_geodis_fr/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /dev/null +++ b/delivery_roulier_geodis_fr/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/delivery_roulier_geodis_fr/__manifest__.py b/delivery_roulier_geodis_fr/__manifest__.py new file mode 100644 index 0000000000..1229852593 --- /dev/null +++ b/delivery_roulier_geodis_fr/__manifest__.py @@ -0,0 +1,29 @@ +# © 2016 Raphael REVERDY +# David BEAL +# Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "Delivery Carrier Geodis (fr)", + "version": "14.0.1.0.0", + "author": "Akretion, Odoo Community Association (OCA)", + "summary": "Generate Label for Geodis logistic", + "maintainer": "Akretion, Odoo Community Association (OCA)", + "category": "Warehouse", + "depends": [ + "delivery_roulier", + "delivery_carrier_deposit", + "delivery_roulier_option", + "l10n_fr_siret", + "base_phone", + ], + "website": "https://github.com/OCA/delivery-carrier", + "data": [ + "data/delivery.xml", + "data/keychain.xml", + "data/sequence_geodis.xml", + ], + "demo": [], + "installable": True, + "license": "AGPL-3", +} diff --git a/delivery_roulier_geodis_fr/data/delivery.xml b/delivery_roulier_geodis_fr/data/delivery.xml new file mode 100644 index 0000000000..efd35f3f40 --- /dev/null +++ b/delivery_roulier_geodis_fr/data/delivery.xml @@ -0,0 +1,164 @@ + + + + + + Geodis + + + + 26 Quai Charles Pasqua + 92300 + Levallois-Perret + + + + + On Demand + RDW + + + + + Geodis Express + EXP + EXP + fixed + service + geodis + + + + + + + Geodis Rapide + RAP + RAP + fixed + service + geodis + + + + + + + Geodis Messagerie + MES + MES + fixed + service + geodis + + + + + + Geodis Top24 + T24 + T24 + fixed + service + geodis + + + + + + + Geodis Calpack + CAL + CAL + fixed + service + geodis + + + + + + + Geodis Pack30 + P30 + P30 + fixed + service + geodis + + + + + + + Geodis InterPack + INP + INP + fixed + service + geodis + + + + + + + Geodis Messagerie Internationnale + MEI + MEI + fixed + service + geodis + + + + + + + Geodis CXI France Express + CXI + CXI + fixed + service + geodis + + + + + + + Geodis CX Expres + CX + CX + fixed + service + geodis + + + + + + + Geodis Inter Express + INE + INE + fixed + service + geodis + + + + + + + Geodis Euro Express + EEX + EEX + fixed + service + geodis + + + + + diff --git a/delivery_roulier_geodis_fr/data/keychain.xml b/delivery_roulier_geodis_fr/data/keychain.xml new file mode 100644 index 0000000000..7826f4c32c --- /dev/null +++ b/delivery_roulier_geodis_fr/data/keychain.xml @@ -0,0 +1,13 @@ + + + + Geodis logistique + roulier_geodis + geodis_logistique + + + Geodis traking + roulier_geodis_tracking + Parcel Tracking + + diff --git a/delivery_roulier_geodis_fr/data/sequence_geodis.xml b/delivery_roulier_geodis_fr/data/sequence_geodis.xml new file mode 100644 index 0000000000..cb0314efe9 --- /dev/null +++ b/delivery_roulier_geodis_fr/data/sequence_geodis.xml @@ -0,0 +1,9 @@ + + + Numerotation des colis geodis + geodis.nrecep.number + %(year)s_ + 8 + no_gap + + diff --git a/delivery_roulier_geodis_fr/models/__init__.py b/delivery_roulier_geodis_fr/models/__init__.py new file mode 100644 index 0000000000..84a2efda58 --- /dev/null +++ b/delivery_roulier_geodis_fr/models/__init__.py @@ -0,0 +1,5 @@ +from . import stock_picking +from . import delivery +from . import deposit +from . import keychain +from . import stock_quant_package diff --git a/delivery_roulier_geodis_fr/models/delivery.py b/delivery_roulier_geodis_fr/models/delivery.py new file mode 100644 index 0000000000..3928239f01 --- /dev/null +++ b/delivery_roulier_geodis_fr/models/delivery.py @@ -0,0 +1,18 @@ +# © 2014 David BEAL @ Akretion +# Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, models + + +class DeliveryCarrier(models.Model): + _inherit = "delivery.carrier" + + @api.model + def _get_carrier_type_selection(self): + """Add Geodis carrier type.""" + res = super(DeliveryCarrier, self)._get_carrier_type_selection() + res.append( + ("geodis", "Geodis"), + ) + return res diff --git a/delivery_roulier_geodis_fr/models/deposit.py b/delivery_roulier_geodis_fr/models/deposit.py new file mode 100644 index 0000000000..15b50ac486 --- /dev/null +++ b/delivery_roulier_geodis_fr/models/deposit.py @@ -0,0 +1,173 @@ +import logging +from base64 import b64encode +from datetime import datetime + +from odoo import models +from odoo.exceptions import UserError +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT +from odoo.tools.translate import _ + +_logger = logging.getLogger(__name__) + +try: + from roulier import roulier + from roulier.exception import InvalidApiInput # CarrierError +except ImportError: + _logger.debug("Cannot `import roulier`.") + + +class DepositSlip(models.Model): + _inherit = "deposit.slip" + + def _geodis_prepare_data(self): + """Create lines for each picking. + + In a EDI file order is important. + Returns a dict per agencies + + @returns [] + """ + self.ensure_one() + + # build a dict of pickings per agencies + def pickings_agencies(pickings): + agencies = {} + for picking in pickings: + account = picking._get_account(None).get_data() + agencies.setdefault( + account["agencyId"], + { + "senders": None, + "pickings": [], + }, + )["pickings"].append(picking) + return agencies + + pickagencies = pickings_agencies(self.picking_ids) + + # build a dict of pickings per sender + def pickings_senders(pickings): + senders = {} + for picking in pickings: + partner = picking._get_sender(None) + senders.setdefault( + partner.id, + {"pickings": [], "account": picking._get_account(None).get_data()}, + )["pickings"].append(picking) + return senders + + for agency_id, pickagency in pickagencies.iteritems(): + pickagency["senders"] = pickings_senders(pickagency["pickings"]) + + # build a response file per agency / sender + files = [] + i = 0 + for agency_id, pickagency in pickagencies.iteritems(): + for sender_id, picksender in pickagency["senders"].iteritems(): + i += 1 + + # consolidate pickings for agency / sender + shipments = [ + picking._geodis_prepare_edi() for picking in picksender["pickings"] + ] + # we need one of the pickings to lookup addresses + picking = picksender["pickings"][0] + from_address = self._geodis_get_from_address(picking) + agency_address = self._geodis_get_agency_address(picking, agency_id) + account = picksender["account"] + + service = { + "depositId": "%s%s" % (self.id, i), + "depositDate": datetime.strptime( + self.create_date, DEFAULT_SERVER_DATETIME_FORMAT + ), + "customerId": account["customerId"], + "interchangeSender": account["interchangeSender"], + "interchangeRecipient": account["interchangeRecipient"], + } + files.append( + { + "shipments": shipments, + "from_address": from_address, + "agency_address": agency_address, + "service": service, + "agency_id": agency_id, + "sender_id": sender_id, + } + ) + return files + + def _geodis_get_from_address(self, picking): + """Return a dict of the sender.""" + partner = picking._get_sender(None) + address = picking._convert_address(partner) + address["siret"] = partner.siret + return address + + def _geodis_get_agency_address(self, picking, agency_id): + """Return a dict the agency.""" + partner = self._geodis_get_agency_partner(picking.carrier_id, agency_id) + address = picking._convert_address(partner) + address["siret"] = partner.siret + return address + + def _geodis_get_agency_partner(self, delivery_carrier_id, agency_id): + """Find a partner given an agency_id. + + An agency is: + - a contact (res.partner) + - child of carrier company + - has ref field agency_id + """ + carrier_hq = delivery_carrier_id.partner_id + carrier_agency = self.env["res.partner"].search( + [["parent_id", "=", carrier_hq.id], ["ref", "=", agency_id]] + ) + # we use internal reference + if not carrier_agency: + _logger.debug("No child agency, fallback to parent") + return carrier_agency or carrier_hq + + def _geodis_create_edi_file(self, payload): + """Create a edi file with headers and data. + + One agency per call. + + params: + payload : roulier.get_api("edi") + return: string + """ + geo = roulier.get("geodis") + try: + edi = geo.get_edi(payload) # io.ByteIO + except InvalidApiInput as e: + raise UserError(_(u"Bad input: %s\n" % e.message)) + return edi + + def _get_geodis_attachment_name(self, idx, payload_agency): + return "%s_%s.txt" % (self.name, idx) + + def _geodis_create_attachments(self): + """Create EDI files in attachment.""" + payloads = self._geodis_prepare_data() + attachments = self.env["ir.attachment"] + for idx, payload_agency in enumerate(payloads, start=1): + edi_file = self._geodis_create_edi_file(payload_agency) + file_name = self._get_geodis_attachment_name(idx, payload_agency) + vals = { + "name": file_name, + "res_id": self.id, + "res_model": "deposit.slip", + "datas": b64encode(edi_file.encode("utf8")), + "datas_fname": file_name, + "type": "binary", + } + attachments += self.env["ir.attachment"].create(vals) + return attachments + + def create_edi_file(self): + self.ensure_one() + if self.carrier_type == "geodis": + return self._geodis_create_attachments() + else: + return super(DepositSlip, self).create_edi_file() diff --git a/delivery_roulier_geodis_fr/models/keychain.py b/delivery_roulier_geodis_fr/models/keychain.py new file mode 100644 index 0000000000..f959bab3a1 --- /dev/null +++ b/delivery_roulier_geodis_fr/models/keychain.py @@ -0,0 +1,44 @@ +# @author Raphael Reverdy +# David BEAL +# EBII MonsieurB +# Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging + +from odoo import fields, models + +_logger = logging.getLogger(__name__) + + +class AccountProduct(models.Model): + _inherit = "keychain.account" + + namespace = fields.Selection( + selection_add=[ + ("roulier_geodis", "Geodis"), + ("roulier_geodis_tracking", "Geodis Tracking"), + ] + ) + + def _roulier_geodis_init_data(self): + return { + "agencyId": "", + "customerId": "", + "labelFormat": "ZPL", + "isTest": True, + "interchangeSender": "", + "interchangeRecipient": "", + "hubId": "", + } + + # dummy methods to be compatible with keychain... + # This will be gone on migration + def _roulier_geodis_validate_data(self, data): + return True + + def _roulier_geodis_tracking_init_data(self): + return {} + + def _roulier_geodis_tracking_validate_data(self, data): + return True diff --git a/delivery_roulier_geodis_fr/models/stock_picking.py b/delivery_roulier_geodis_fr/models/stock_picking.py new file mode 100644 index 0000000000..d9e286e3cf --- /dev/null +++ b/delivery_roulier_geodis_fr/models/stock_picking.py @@ -0,0 +1,242 @@ +# @author Raphael Reverdy +# David BEAL +# Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging +from datetime import datetime, timedelta + +from odoo import _, api, fields, models +from odoo.exceptions import Warning as UserError +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT + +_logger = logging.getLogger(__name__) +try: + from roulier import roulier + from roulier.exception import CarrierError, InvalidApiInput +except ImportError: + _logger.debug("Cannot `import roulier`.") + + +GEODIS_DEFAULT_PRIORITY = { + "MES": "3", + "MEI": "3", + "CXI": "1", + "CX": "1", + "EEX": "1", +} + +GEODIS_DEFAULT_TOD = { + "MES": "P", + "MEI": "DAP", + "CXI": "P", + "EEX": "DAP", +} + +ADDRESS_ERROR_CODES = ["C0041", "C0042", "C0044", "C0045", "C0047", "T0023"] + + +class StockPicking(models.Model): + _inherit = "stock.picking" + geodis_shippingid = fields.Char(help="Shipping Id in Geodis terminology") + + def _geodis_get_shipping_date(self, package_id): + """Estimate shipping date.""" + self.ensure_one() + shipping_date = self.min_date + if self.date_done: + shipping_date = self.date_done + + shipping_date = datetime.strptime(shipping_date, DEFAULT_SERVER_DATETIME_FORMAT) + + tomorrow = datetime.now() + timedelta(1) + if shipping_date < tomorrow: + # don't send in the past + shipping_date = tomorrow + + return shipping_date.strftime("%Y%m%d") + + def _geodis_convert_address(self, partner): + """Truncate address and name to 35 chars.""" + address = self._roulier_convert_address(partner) or {} + # get_split_adress from partner_helper module + streets = partner._get_split_address(partner, 3, 35) + address["street1"], address["street2"], address["street3"] = streets + for field in ("name", "city"): + address[field] = address[field][0:35] + return address + + def _geodis_get_priority(self, package): + """Define options for the shippment.""" + return GEODIS_DEFAULT_PRIORITY.get(self.carrier_code, "") + + def _geodis_get_options(self, package): + """Compact geodis options. + + Options are passed as string. it obey some custom + binding like RDW + AAO = AWO. + It should be implemented here. For the moment, only + one option can be passed. + """ + options = self._roulier_get_options(package) + actives = [option for option in options.keys() if options[option]] + return actives and actives[0] or "" + + def _geodis_get_notifications(self, package): + options = self._get_options(package) + recipient = self._convert_address(self._get_receiver(package)) + if "RDW" in options: + if recipient["email"]: + if recipient["phone"]: + return "P" + else: + return "M" + else: + if recipient["phone"]: + return "S" + else: + raise UserError(_("Can't set up a rendez-vous wihout mail or tel")) + + def _geodis_get_service(self, package): + service = self._roulier_get_service(package) + service["option"] = self._get_options(package) + service["notifications"] = self._geodis_get_notifications(package) + return service + + def _geodis_prepare_edi(self): + """Return a list.""" + self.ensure_one() + picking = self + + packages = picking._get_packages_from_picking() + parcels = [ + {"barcode": pack.geodis_cab, "weight": pack.weight} for pack in packages + ] + + return { + "product": picking.carrier_code, + "productOption": picking._get_options(None), + "productPriority": picking._geodis_get_priority(None), + "notifications": picking._geodis_get_notifications(None), + "productTOD": GEODIS_DEFAULT_TOD[picking.carrier_code], + "to_address": self._convert_address(picking._get_receiver(None)), + "reference1": picking.origin, + "reference2": "", + "reference3": "", + "shippingId": picking.geodis_shippingid, + "parcels": parcels, + } + + def _geodis_get_address_proposition(self, raise_address=True): + # check address + self.ensure_one() + package = self.env["stock.quant.package"].new({}) + package.carrier_id = self.carrier_id + roulier_instance = roulier.get(self.carrier_type) + payload = roulier_instance.api("findLocalite") + receiver = self._get_receiver(package) + payload["auth"] = self._get_auth(package) + payload["to_address"] = self._convert_address(receiver) + account = self._get_account(self) + service = account.get_data() + payload["service"] = {"is_test": service.get("isTest", False)} + addresses = [] + try: + # api call + addresses = roulier_instance.get(payload, "findLocalite") + except InvalidApiInput as e: + raise UserError(package._invalid_api_input_handling(payload, e)) + except CarrierError as e: + if ( + e.message + and e.message[0].get("id") + and not raise_address + and e.message[0].get("id") in ADDRESS_ERROR_CODES + ): + return addresses + else: + raise UserError(package._carrier_error_handling(payload, e)) + return addresses + + def _geodis_check_address(self): + self.ensure_one() + addresses = self._geodis_get_address_proposition() + return len(addresses) == 1 + + def _gen_shipping_id(self): + """Generate a shipping id. + + Shipping id is persisted on the picking and it's + calculated from a sequence since it should be + 8 char long and unique for at least 1 year + """ + + def gen_id(): + sequence = self.env["ir.sequence"].next_by_code("geodis.nrecep.number") + # this is prefixed by year_ so we split it befor use + year, number = sequence.split("_") + # pad with 0 to build an 8digits number (string) + return "%08d" % int(number) + + for picking in self: + picking.geodis_shippingid = picking.geodis_shippingid or gen_id() + return True + + def _geodis_update_tracking(self): + geo = roulier.get("geodis") + success_pickings = self.env["stock.picking"] + for rec in self: + packages = self._get_packages_from_picking() + account = rec._geodis_get_auth_tracking(packages) + payload = { + "auth": account, + "service": {"shippingId": rec.geodis_shippingid}, + } + + ret = geo.get_tracking_list(payload) + + if len(ret) != 1: + _logger.warning("Geodis tracking not found. Picking %s" % rec.id) + continue + # multipack not implemented yet + data = ret[0] + packages.write( + { + "geodis_tracking_url": data["tracking"]["publicUrl"], + "parcel_tracking": data["tracking"]["trackingCode"], + } + ) + success_pickings |= rec + return success_pickings + + def _geodis_get_auth_tracking(self, packages): + """Because it's not the same credentials than + get_label.""" + + account = self._geodis_get_account_tracking(packages) + auth = { + "login": account.login, + "password": account._get_password(), + } + return auth + + def _geodis_get_account_tracking(self, package): + """Return an 'account'. + + By default, the first account encoutered for this type. + Depending on your case, you may store it on the picking or + compute it from your business rules. + + Accounts are resolved at runtime (can be != for dev/prod) + + + We have namespace = roulier_geodis for get_label + and roulier_geodis_tracking only for tracking + """ + keychain = self.env["keychain.account"] + if self.env.user.has_group("stock.group_stock_user"): + retrieve = keychain.sudo().retrieve + else: + retrieve = keychain.retrieve + accounts = retrieve([["namespace", "=", "roulier_geodis_tracking"]]) + return accounts[0] diff --git a/delivery_roulier_geodis_fr/models/stock_quant_package.py b/delivery_roulier_geodis_fr/models/stock_quant_package.py new file mode 100644 index 0000000000..014893a16c --- /dev/null +++ b/delivery_roulier_geodis_fr/models/stock_quant_package.py @@ -0,0 +1,64 @@ +# @author Raphael Reverdy +# David BEAL +# EBII MonsieurB +# Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging + +from odoo import api, fields, models + +_logger = logging.getLogger(__name__) + + +class StockQuantPackage(models.Model): + _inherit = "stock.quant.package" + + geodis_cab = fields.Char(help="Barcode of the label") + geodis_tracking_url = fields.Char(help="Public url for parcel tracking") + + def _geodis_generate_labels(self, picking): + packages = self + response = packages._call_roulier_api(picking) + packages._handle_tracking(picking, response) + packages._handle_attachments(picking, response) + + def _geodis_get_parcels(self, picking): + return [pack._get_parcel(picking) for pack in self] + + def _geodis_before_call(self, picking, request): + # TODO _get_options is called fo each package by the result + # is the same. Should be store after first call + picking._gen_shipping_id() # explicit generation + account = picking._get_account(self) + service = account.get_data() + request["service"]["customerId"] = service["customerId"] + request["service"]["agencyId"] = service["agencyId"] + request["service"]["hubId"] = service["hubId"] + request["service"]["labelFormat"] = service["labelFormat"] + request["service"]["shippingId"] = picking.geodis_shippingid + request["service"]["is_test"] = service["isTest"] + return request + + def _geodis_handle_tracking(self, picking, response): + i = 0 + for rec in self: + rec.write( + { + "geodis_cab": response["parcels"][i]["number"], + "parcel_tracking": picking.geodis_shippingid, + } + ) + i += 1 + + def _geodis_should_include_customs(self, picking): + """Customs documents not implemented.""" + return False + + def _geodis_carrier_error_handling(self, payload, exception): + pay = payload + pay["auth"]["password"] = "****" + return self._roulier_carrier_error_handling(payload, exception) + + def _geodis_get_tracking_link(self): + return self.geodis_tracking_url diff --git a/delivery_roulier_geodis_fr/static/src/img/icon.png b/delivery_roulier_geodis_fr/static/src/img/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..353b58000ebd9dd126162e73a2048419b7e18cb5 GIT binary patch literal 3338 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 U7>fyGH~;_u07*qoM6N<$f?DEWRsaA1 literal 0 HcmV?d00001 diff --git a/delivery_roulier_geodis_fr/static/src/img/logo.jpg b/delivery_roulier_geodis_fr/static/src/img/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2ab9055297df0e95570d225ff7ac84ea5d8a868c GIT binary patch literal 3895 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 Date: Wed, 2 Jun 2021 16:38:17 +0200 Subject: [PATCH 02/13] Migrate delivery_roulier_geodis_fr --- delivery_roulier_geodis_fr/__manifest__.py | 9 +- delivery_roulier_geodis_fr/data/delivery.xml | 140 +++++++---------- delivery_roulier_geodis_fr/data/keychain.xml | 13 -- delivery_roulier_geodis_fr/data/product.xml | 10 ++ delivery_roulier_geodis_fr/models/__init__.py | 5 +- .../models/carrier_account.py | 16 ++ delivery_roulier_geodis_fr/models/delivery.py | 18 --- .../models/delivery_carrier.py | 12 ++ .../models/delivery_carrier_agency.py | 11 ++ delivery_roulier_geodis_fr/models/deposit.py | 74 +++------ delivery_roulier_geodis_fr/models/keychain.py | 44 ------ .../models/stock_picking.py | 141 ++++++++---------- .../models/stock_quant_package.py | 45 +----- delivery_roulier_geodis_fr/tests/__init__.py | 1 + .../GeodisFrLabelCase.test_addresses.yaml | 105 +++++++++++++ .../GeodisFrLabelCase.test_labels.yaml | 95 ++++++++++++ .../tests/test_geodis_labels.py | 117 +++++++++++++++ .../views/carrier_account_views.xml | 28 ++++ .../views/delivery_carrier_agency_views.xml | 28 ++++ 19 files changed, 576 insertions(+), 336 deletions(-) delete mode 100644 delivery_roulier_geodis_fr/data/keychain.xml create mode 100644 delivery_roulier_geodis_fr/data/product.xml create mode 100644 delivery_roulier_geodis_fr/models/carrier_account.py delete mode 100644 delivery_roulier_geodis_fr/models/delivery.py create mode 100644 delivery_roulier_geodis_fr/models/delivery_carrier.py create mode 100644 delivery_roulier_geodis_fr/models/delivery_carrier_agency.py delete mode 100644 delivery_roulier_geodis_fr/models/keychain.py create mode 100644 delivery_roulier_geodis_fr/tests/__init__.py create mode 100644 delivery_roulier_geodis_fr/tests/cassettes/GeodisFrLabelCase.test_addresses.yaml create mode 100644 delivery_roulier_geodis_fr/tests/cassettes/GeodisFrLabelCase.test_labels.yaml create mode 100644 delivery_roulier_geodis_fr/tests/test_geodis_labels.py create mode 100644 delivery_roulier_geodis_fr/views/carrier_account_views.xml create mode 100644 delivery_roulier_geodis_fr/views/delivery_carrier_agency_views.xml diff --git a/delivery_roulier_geodis_fr/__manifest__.py b/delivery_roulier_geodis_fr/__manifest__.py index 1229852593..57975a12ac 100644 --- a/delivery_roulier_geodis_fr/__manifest__.py +++ b/delivery_roulier_geodis_fr/__manifest__.py @@ -1,6 +1,3 @@ -# © 2016 Raphael REVERDY -# David BEAL -# Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { @@ -12,16 +9,18 @@ "category": "Warehouse", "depends": [ "delivery_roulier", + "delivery_carrier_agency", "delivery_carrier_deposit", "delivery_roulier_option", "l10n_fr_siret", - "base_phone", ], "website": "https://github.com/OCA/delivery-carrier", "data": [ + "data/product.xml", "data/delivery.xml", - "data/keychain.xml", "data/sequence_geodis.xml", + "views/carrier_account_views.xml", + "views/delivery_carrier_agency_views.xml", ], "demo": [], "installable": True, diff --git a/delivery_roulier_geodis_fr/data/delivery.xml b/delivery_roulier_geodis_fr/data/delivery.xml index efd35f3f40..c386d80ec7 100644 --- a/delivery_roulier_geodis_fr/data/delivery.xml +++ b/delivery_roulier_geodis_fr/data/delivery.xml @@ -1,17 +1,42 @@ - + Geodis - - 26 Quai Charles Pasqua 92300 + +33156762600 Levallois-Perret + + + Geodis Lille Europe + + 7 Avenue de la Rotonde + 59160 + +33320085555 + + Lomme + 457507358 + 00044 + + + + + France Express Lille + + 2 RUE DES SAPINS + 59810 + +336320161718 + LESQUIN + 300089174 + + 00035 + + On Demand @@ -21,144 +46,85 @@ Geodis Express - EXP EXP - fixed - service - geodis - - - + geodis_fr + Geodis Rapide - RAP + geodis_fr + RAP - fixed - service - geodis - - - Geodis Messagerie - MES + geodis_fr + MES - fixed - service - geodis - - - Geodis Top24 - T24 + geodis_fr + T24 - fixed - service - geodis - - - Geodis Calpack - CAL + geodis_fr + CAL - fixed - service - geodis - - - Geodis Pack30 - P30 + geodis_fr + P30 - fixed - service - geodis - - - Geodis InterPack - INP + geodis_fr + INP - fixed - service - geodis - - - Geodis Messagerie Internationnale - MEI + geodis_fr + MEI - fixed - service - geodis - - - Geodis CXI France Express - CXI + geodis_fr + CXI - fixed - service - geodis - - - Geodis CX Expres - CX + geodis_fr + CX - fixed - service - geodis - - - Geodis Inter Express - INE + geodis_fr + INE - fixed - service - geodis - - - Geodis Euro Express - EEX + geodis_fr + EEX - fixed - service - geodis - - - + diff --git a/delivery_roulier_geodis_fr/data/keychain.xml b/delivery_roulier_geodis_fr/data/keychain.xml deleted file mode 100644 index 7826f4c32c..0000000000 --- a/delivery_roulier_geodis_fr/data/keychain.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - Geodis logistique - roulier_geodis - geodis_logistique - - - Geodis traking - roulier_geodis_tracking - Parcel Tracking - - diff --git a/delivery_roulier_geodis_fr/data/product.xml b/delivery_roulier_geodis_fr/data/product.xml new file mode 100644 index 0000000000..d260d97f82 --- /dev/null +++ b/delivery_roulier_geodis_fr/data/product.xml @@ -0,0 +1,10 @@ + + + + + SHIP_GEODIS + service + Coûts de livraison - GEODIS + + + diff --git a/delivery_roulier_geodis_fr/models/__init__.py b/delivery_roulier_geodis_fr/models/__init__.py index 84a2efda58..f550ad0bc9 100644 --- a/delivery_roulier_geodis_fr/models/__init__.py +++ b/delivery_roulier_geodis_fr/models/__init__.py @@ -1,5 +1,6 @@ from . import stock_picking -from . import delivery +from . import delivery_carrier +from . import carrier_account +from . import delivery_carrier_agency from . import deposit -from . import keychain from . import stock_quant_package diff --git a/delivery_roulier_geodis_fr/models/carrier_account.py b/delivery_roulier_geodis_fr/models/carrier_account.py new file mode 100644 index 0000000000..1b45cc2992 --- /dev/null +++ b/delivery_roulier_geodis_fr/models/carrier_account.py @@ -0,0 +1,16 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class CarrierAccount(models.Model): + _inherit = "carrier.account" + + geodis_fr_customer_id = fields.Char(string="Customer Id") + geodis_fr_file_format = fields.Selection( + [("ZPL", "ZPL")], default="ZPL", string="File Format" + ) + geodis_fr_tracking_account = fields.Boolean( + string="Is a Tracking Account", + help="Check this box if this account is used to get the tracking links for geodis", + ) diff --git a/delivery_roulier_geodis_fr/models/delivery.py b/delivery_roulier_geodis_fr/models/delivery.py deleted file mode 100644 index 3928239f01..0000000000 --- a/delivery_roulier_geodis_fr/models/delivery.py +++ /dev/null @@ -1,18 +0,0 @@ -# © 2014 David BEAL @ Akretion -# Sébastien BEAU -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from odoo import api, models - - -class DeliveryCarrier(models.Model): - _inherit = "delivery.carrier" - - @api.model - def _get_carrier_type_selection(self): - """Add Geodis carrier type.""" - res = super(DeliveryCarrier, self)._get_carrier_type_selection() - res.append( - ("geodis", "Geodis"), - ) - return res diff --git a/delivery_roulier_geodis_fr/models/delivery_carrier.py b/delivery_roulier_geodis_fr/models/delivery_carrier.py new file mode 100644 index 0000000000..b38918e76d --- /dev/null +++ b/delivery_roulier_geodis_fr/models/delivery_carrier.py @@ -0,0 +1,12 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class DeliveryCarrier(models.Model): + _inherit = "delivery.carrier" + + delivery_type = fields.Selection( + selection_add=[("geodis_fr", "Geodis")], + ondelete={"geodis_fr": "set default"}, + ) diff --git a/delivery_roulier_geodis_fr/models/delivery_carrier_agency.py b/delivery_roulier_geodis_fr/models/delivery_carrier_agency.py new file mode 100644 index 0000000000..ac1bbf8b66 --- /dev/null +++ b/delivery_roulier_geodis_fr/models/delivery_carrier_agency.py @@ -0,0 +1,11 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class DeliveryCarrierAgency(models.Model): + _inherit = "delivery.carrier.agency" + + geodis_fr_interchange_sender = fields.Char() + geodis_fr_interchange_recipient = fields.Char() + geodis_fr_hub_id = fields.Char() diff --git a/delivery_roulier_geodis_fr/models/deposit.py b/delivery_roulier_geodis_fr/models/deposit.py index 15b50ac486..50a845883c 100644 --- a/delivery_roulier_geodis_fr/models/deposit.py +++ b/delivery_roulier_geodis_fr/models/deposit.py @@ -1,10 +1,8 @@ import logging from base64 import b64encode -from datetime import datetime from odoo import models from odoo.exceptions import UserError -from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT from odoo.tools.translate import _ _logger = logging.getLogger(__name__) @@ -33,14 +31,10 @@ def _geodis_prepare_data(self): def pickings_agencies(pickings): agencies = {} for picking in pickings: - account = picking._get_account(None).get_data() - agencies.setdefault( - account["agencyId"], - { - "senders": None, - "pickings": [], - }, - )["pickings"].append(picking) + agency = picking._get_carrier_agency() + agencies.setdefault(agency, {"senders": None, "pickings": []},)[ + "pickings" + ].append(picking) return agencies pickagencies = pickings_agencies(self.picking_ids) @@ -52,38 +46,37 @@ def pickings_senders(pickings): partner = picking._get_sender(None) senders.setdefault( partner.id, - {"pickings": [], "account": picking._get_account(None).get_data()}, + {"pickings": [], "account": picking._get_account()}, )["pickings"].append(picking) return senders - for agency_id, pickagency in pickagencies.iteritems(): + for _agency, pickagency in pickagencies.items(): pickagency["senders"] = pickings_senders(pickagency["pickings"]) # build a response file per agency / sender files = [] i = 0 - for agency_id, pickagency in pickagencies.iteritems(): - for sender_id, picksender in pickagency["senders"].iteritems(): + for agency, pickagency in pickagencies.items(): + for sender_id, picksender in pickagency["senders"].items(): i += 1 # consolidate pickings for agency / sender shipments = [ - picking._geodis_prepare_edi() for picking in picksender["pickings"] + picking._geodis_fr_prepare_edi() + for picking in picksender["pickings"] ] # we need one of the pickings to lookup addresses picking = picksender["pickings"][0] from_address = self._geodis_get_from_address(picking) - agency_address = self._geodis_get_agency_address(picking, agency_id) + agency_address = self._geodis_get_agency_address(picking, agency) account = picksender["account"] service = { "depositId": "%s%s" % (self.id, i), - "depositDate": datetime.strptime( - self.create_date, DEFAULT_SERVER_DATETIME_FORMAT - ), - "customerId": account["customerId"], - "interchangeSender": account["interchangeSender"], - "interchangeRecipient": account["interchangeRecipient"], + "depositDate": self.create_date, + "customerId": account.geodis_fr_customer_id, + "interchangeSender": agency.geodis_fr_interchange_sender, + "interchangeRecipient": agency.geodis_fr_interchange_recipient, } files.append( { @@ -91,7 +84,7 @@ def pickings_senders(pickings): "from_address": from_address, "agency_address": agency_address, "service": service, - "agency_id": agency_id, + "agency_id": agency.external_reference, "sender_id": sender_id, } ) @@ -101,33 +94,16 @@ def _geodis_get_from_address(self, picking): """Return a dict of the sender.""" partner = picking._get_sender(None) address = picking._convert_address(partner) - address["siret"] = partner.siret + address["siret"] = partner.siret or "" return address - def _geodis_get_agency_address(self, picking, agency_id): + def _geodis_get_agency_address(self, picking, agency): """Return a dict the agency.""" - partner = self._geodis_get_agency_partner(picking.carrier_id, agency_id) + partner = agency.partner_id address = picking._convert_address(partner) - address["siret"] = partner.siret + address["siret"] = partner.siret or "" return address - def _geodis_get_agency_partner(self, delivery_carrier_id, agency_id): - """Find a partner given an agency_id. - - An agency is: - - a contact (res.partner) - - child of carrier company - - has ref field agency_id - """ - carrier_hq = delivery_carrier_id.partner_id - carrier_agency = self.env["res.partner"].search( - [["parent_id", "=", carrier_hq.id], ["ref", "=", agency_id]] - ) - # we use internal reference - if not carrier_agency: - _logger.debug("No child agency, fallback to parent") - return carrier_agency or carrier_hq - def _geodis_create_edi_file(self, payload): """Create a edi file with headers and data. @@ -137,11 +113,10 @@ def _geodis_create_edi_file(self, payload): payload : roulier.get_api("edi") return: string """ - geo = roulier.get("geodis") try: - edi = geo.get_edi(payload) # io.ByteIO + edi = roulier.get(self.delivery_type, "get_edi", payload) except InvalidApiInput as e: - raise UserError(_(u"Bad input: %s\n" % e.message)) + raise UserError(_(u"Bad input: %s\n" % str(e))) return edi def _get_geodis_attachment_name(self, idx, payload_agency): @@ -159,7 +134,6 @@ def _geodis_create_attachments(self): "res_id": self.id, "res_model": "deposit.slip", "datas": b64encode(edi_file.encode("utf8")), - "datas_fname": file_name, "type": "binary", } attachments += self.env["ir.attachment"].create(vals) @@ -167,7 +141,7 @@ def _geodis_create_attachments(self): def create_edi_file(self): self.ensure_one() - if self.carrier_type == "geodis": + if self.delivery_type == "geodis_fr": return self._geodis_create_attachments() else: - return super(DepositSlip, self).create_edi_file() + return super().create_edi_file() diff --git a/delivery_roulier_geodis_fr/models/keychain.py b/delivery_roulier_geodis_fr/models/keychain.py deleted file mode 100644 index f959bab3a1..0000000000 --- a/delivery_roulier_geodis_fr/models/keychain.py +++ /dev/null @@ -1,44 +0,0 @@ -# @author Raphael Reverdy -# David BEAL -# EBII MonsieurB -# Sébastien BEAU -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -import logging - -from odoo import fields, models - -_logger = logging.getLogger(__name__) - - -class AccountProduct(models.Model): - _inherit = "keychain.account" - - namespace = fields.Selection( - selection_add=[ - ("roulier_geodis", "Geodis"), - ("roulier_geodis_tracking", "Geodis Tracking"), - ] - ) - - def _roulier_geodis_init_data(self): - return { - "agencyId": "", - "customerId": "", - "labelFormat": "ZPL", - "isTest": True, - "interchangeSender": "", - "interchangeRecipient": "", - "hubId": "", - } - - # dummy methods to be compatible with keychain... - # This will be gone on migration - def _roulier_geodis_validate_data(self, data): - return True - - def _roulier_geodis_tracking_init_data(self): - return {} - - def _roulier_geodis_tracking_validate_data(self, data): - return True diff --git a/delivery_roulier_geodis_fr/models/stock_picking.py b/delivery_roulier_geodis_fr/models/stock_picking.py index d9e286e3cf..ac4a41ce4c 100644 --- a/delivery_roulier_geodis_fr/models/stock_picking.py +++ b/delivery_roulier_geodis_fr/models/stock_picking.py @@ -1,14 +1,9 @@ -# @author Raphael Reverdy -# David BEAL -# Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging -from datetime import datetime, timedelta -from odoo import _, api, fields, models +from odoo import _, fields, models from odoo.exceptions import Warning as UserError -from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT _logger = logging.getLogger(__name__) try: @@ -38,39 +33,24 @@ class StockPicking(models.Model): _inherit = "stock.picking" - geodis_shippingid = fields.Char(help="Shipping Id in Geodis terminology") - - def _geodis_get_shipping_date(self, package_id): - """Estimate shipping date.""" - self.ensure_one() - shipping_date = self.min_date - if self.date_done: - shipping_date = self.date_done - shipping_date = datetime.strptime(shipping_date, DEFAULT_SERVER_DATETIME_FORMAT) - - tomorrow = datetime.now() + timedelta(1) - if shipping_date < tomorrow: - # don't send in the past - shipping_date = tomorrow - - return shipping_date.strftime("%Y%m%d") + geodis_shippingid = fields.Char(help="Shipping Id in Geodis terminology") - def _geodis_convert_address(self, partner): + def _geodis_fr_convert_address(self, partner): """Truncate address and name to 35 chars.""" address = self._roulier_convert_address(partner) or {} # get_split_adress from partner_helper module - streets = partner._get_split_address(partner, 3, 35) + streets = partner._get_split_address(3, 35) address["street1"], address["street2"], address["street3"] = streets for field in ("name", "city"): address[field] = address[field][0:35] return address - def _geodis_get_priority(self, package): + def _geodis_fr_get_priority(self, package): """Define options for the shippment.""" return GEODIS_DEFAULT_PRIORITY.get(self.carrier_code, "") - def _geodis_get_options(self, package): + def _geodis_fr_get_options(self, package): """Compact geodis options. Options are passed as string. it obey some custom @@ -82,7 +62,7 @@ def _geodis_get_options(self, package): actives = [option for option in options.keys() if options[option]] return actives and actives[0] or "" - def _geodis_get_notifications(self, package): + def _geodis_fr_get_notifications(self, package): options = self._get_options(package) recipient = self._convert_address(self._get_receiver(package)) if "RDW" in options: @@ -97,70 +77,79 @@ def _geodis_get_notifications(self, package): else: raise UserError(_("Can't set up a rendez-vous wihout mail or tel")) - def _geodis_get_service(self, package): - service = self._roulier_get_service(package) + def _geodis_fr_get_service(self, account, package=None): + service = self._roulier_get_service(account, package=package) + agency = self._get_carrier_agency() + service["option"] = self._get_options(package) - service["notifications"] = self._geodis_get_notifications(package) + service["notifications"] = self._geodis_fr_get_notifications(package) + service["customerId"] = account.geodis_fr_customer_id + service["agencyId"] = agency.external_reference + service["hubId"] = agency.geodis_fr_hub_id + self._gen_shipping_id() # explicit generation + service["shippingId"] = self.geodis_shippingid return service - def _geodis_prepare_edi(self): + def _geodis_fr_prepare_edi(self): """Return a list.""" self.ensure_one() picking = self - packages = picking._get_packages_from_picking() + packages = picking.package_ids parcels = [ - {"barcode": pack.geodis_cab, "weight": pack.weight} for pack in packages + {"barcode": pack.geodis_cab, "weight": pack.shipping_weight or pack.weight} + for pack in packages ] return { - "product": picking.carrier_code, + "product": picking.carrier_id.code, "productOption": picking._get_options(None), - "productPriority": picking._geodis_get_priority(None), - "notifications": picking._geodis_get_notifications(None), + "productPriority": picking._geodis_fr_get_priority(None), + "notifications": picking._geodis_fr_get_notifications(None), "productTOD": GEODIS_DEFAULT_TOD[picking.carrier_code], "to_address": self._convert_address(picking._get_receiver(None)), - "reference1": picking.origin, + "reference1": picking.origin or picking.group_id.name or picking.name, "reference2": "", "reference3": "", "shippingId": picking.geodis_shippingid, "parcels": parcels, } - def _geodis_get_address_proposition(self, raise_address=True): + def _geodis_fr_get_address_proposition(self, raise_address=True): # check address self.ensure_one() - package = self.env["stock.quant.package"].new({}) - package.carrier_id = self.carrier_id - roulier_instance = roulier.get(self.carrier_type) - payload = roulier_instance.api("findLocalite") - receiver = self._get_receiver(package) - payload["auth"] = self._get_auth(package) + payload = {} + receiver = self._get_receiver() + account = self._get_account() + payload["auth"] = self._get_auth(account) payload["to_address"] = self._convert_address(receiver) - account = self._get_account(self) - service = account.get_data() - payload["service"] = {"is_test": service.get("isTest", False)} + payload["service"] = {"is_test": not self.carrier_id.prod_environment} addresses = [] try: # api call - addresses = roulier_instance.get(payload, "findLocalite") + addresses = roulier.get(self.delivery_type, "validate_address", payload) except InvalidApiInput as e: - raise UserError(package._invalid_api_input_handling(payload, e)) + raise UserError( + self.env["stock.quant.package"]._invalid_api_input_handling(payload, e) + ) except CarrierError as e: + errors = e.args and e.args[0] if ( - e.message - and e.message[0].get("id") + errors + and errors[0].get("id") and not raise_address - and e.message[0].get("id") in ADDRESS_ERROR_CODES + and errors[0].get("id") in ADDRESS_ERROR_CODES ): return addresses else: + package = self.env["stock.quant.package"].new({}) + package.carrier_id = self.carrier_id raise UserError(package._carrier_error_handling(payload, e)) return addresses - def _geodis_check_address(self): + def _geodis_fr_check_address(self): self.ensure_one() - addresses = self._geodis_get_address_proposition() + addresses = self._geodis_fr_get_address_proposition() return len(addresses) == 1 def _gen_shipping_id(self): @@ -182,18 +171,16 @@ def gen_id(): picking.geodis_shippingid = picking.geodis_shippingid or gen_id() return True - def _geodis_update_tracking(self): - geo = roulier.get("geodis") + def _geodis_fr_update_tracking(self): success_pickings = self.env["stock.picking"] for rec in self: - packages = self._get_packages_from_picking() - account = rec._geodis_get_auth_tracking(packages) + packages = rec.package_ids + account = rec._geodis_fr_get_auth_tracking() payload = { "auth": account, - "service": {"shippingId": rec.geodis_shippingid}, + "tracking": {"shippingId": rec.geodis_shippingid}, } - - ret = geo.get_tracking_list(payload) + ret = roulier.get(rec.delivery_type, "get_tracking_list", payload) if len(ret) != 1: _logger.warning("Geodis tracking not found. Picking %s" % rec.id) @@ -202,41 +189,37 @@ def _geodis_update_tracking(self): data = ret[0] packages.write( { - "geodis_tracking_url": data["tracking"]["publicUrl"], + "parcel_tracking_uri": data["tracking"]["publicUrl"], "parcel_tracking": data["tracking"]["trackingCode"], } ) success_pickings |= rec return success_pickings - def _geodis_get_auth_tracking(self, packages): + def _geodis_fr_get_auth_tracking(self): """Because it's not the same credentials than get_label.""" - account = self._geodis_get_account_tracking(packages) + account = self._geodis_fr_get_account_tracking() auth = { - "login": account.login, - "password": account._get_password(), + "login": account.account, + "password": account.password, } return auth - def _geodis_get_account_tracking(self, package): + def _geodis_fr_get_account_tracking(self): """Return an 'account'. By default, the first account encoutered for this type. Depending on your case, you may store it on the picking or compute it from your business rules. - Accounts are resolved at runtime (can be != for dev/prod) - - - We have namespace = roulier_geodis for get_label - and roulier_geodis_tracking only for tracking """ - keychain = self.env["keychain.account"] - if self.env.user.has_group("stock.group_stock_user"): - retrieve = keychain.sudo().retrieve - else: - retrieve = keychain.retrieve - accounts = retrieve([["namespace", "=", "roulier_geodis_tracking"]]) - return accounts[0] + account = self.env["carrier.account"].search( + [ + ("delivery_type", "=", self.carrier_id.delivery_type), + ("geodis_fr_tracking_account", "=", True), + ], + limit=1, + ) + return account diff --git a/delivery_roulier_geodis_fr/models/stock_quant_package.py b/delivery_roulier_geodis_fr/models/stock_quant_package.py index 014893a16c..f07dc01077 100644 --- a/delivery_roulier_geodis_fr/models/stock_quant_package.py +++ b/delivery_roulier_geodis_fr/models/stock_quant_package.py @@ -1,12 +1,8 @@ -# @author Raphael Reverdy -# David BEAL -# EBII MonsieurB -# Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging -from odoo import api, fields, models +from odoo import fields, models _logger = logging.getLogger(__name__) @@ -15,32 +11,9 @@ class StockQuantPackage(models.Model): _inherit = "stock.quant.package" geodis_cab = fields.Char(help="Barcode of the label") - geodis_tracking_url = fields.Char(help="Public url for parcel tracking") - def _geodis_generate_labels(self, picking): - packages = self - response = packages._call_roulier_api(picking) - packages._handle_tracking(picking, response) - packages._handle_attachments(picking, response) - - def _geodis_get_parcels(self, picking): - return [pack._get_parcel(picking) for pack in self] - - def _geodis_before_call(self, picking, request): - # TODO _get_options is called fo each package by the result - # is the same. Should be store after first call - picking._gen_shipping_id() # explicit generation - account = picking._get_account(self) - service = account.get_data() - request["service"]["customerId"] = service["customerId"] - request["service"]["agencyId"] = service["agencyId"] - request["service"]["hubId"] = service["hubId"] - request["service"]["labelFormat"] = service["labelFormat"] - request["service"]["shippingId"] = picking.geodis_shippingid - request["service"]["is_test"] = service["isTest"] - return request - - def _geodis_handle_tracking(self, picking, response): + def _geodis_fr_parse_response(self, picking, response): + res = self._roulier_parse_response(picking, response) i = 0 for rec in self: rec.write( @@ -50,15 +23,11 @@ def _geodis_handle_tracking(self, picking, response): } ) i += 1 + return res - def _geodis_should_include_customs(self, picking): + def _geodis_fr_should_include_customs(self, picking): """Customs documents not implemented.""" return False - def _geodis_carrier_error_handling(self, payload, exception): - pay = payload - pay["auth"]["password"] = "****" - return self._roulier_carrier_error_handling(payload, exception) - - def _geodis_get_tracking_link(self): - return self.geodis_tracking_url + def _geodis_fr_get_tracking_link(self): + return self.parcel_tracking_uri diff --git a/delivery_roulier_geodis_fr/tests/__init__.py b/delivery_roulier_geodis_fr/tests/__init__.py new file mode 100644 index 0000000000..5a61787495 --- /dev/null +++ b/delivery_roulier_geodis_fr/tests/__init__.py @@ -0,0 +1 @@ +from . import test_geodis_labels diff --git a/delivery_roulier_geodis_fr/tests/cassettes/GeodisFrLabelCase.test_addresses.yaml b/delivery_roulier_geodis_fr/tests/cassettes/GeodisFrLabelCase.test_addresses.yaml new file mode 100644 index 0000000000..e18838fc95 --- /dev/null +++ b/delivery_roulier_geodis_fr/tests/cassettes/GeodisFrLabelCase.test_addresses.yaml @@ -0,0 +1,105 @@ +interactions: +- request: + body: "\n\n\t\n\t\t\n\t000000\n\tpassword\ + \ \n\n\t\n\t\n\t\t\n FR\n\ + \ 69100\n VILLEURBANNE\n\ + \n\n\t\n" + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '678' + SOAPAction: + - + User-Agent: + - python-requests/2.21.0 + content-type: + - text/xml + method: POST + uri: https://espace-rct.geodis.com/geolabel/services/RechercherLocalite + response: + body: + string: "--MIMEBoundary_a17599f2fbb7c3a49741ffca180b26ffa04943f006c90664\r\n\ + Content-Type: text/xml; charset=UTF-8\r\nContent-Transfer-Encoding: binary\r\ + \nContent-ID: <0.517599f2fbb7c3a49741ffca180b26ffa04943f006c90664@apache.org>\r\ + \n\r\n303506969100VILLEURBANNE\r\ + \n--MIMEBoundary_a17599f2fbb7c3a49741ffca180b26ffa04943f006c90664--\r\n" + headers: + Connection: + - keep-alive + Content-Type: + - multipart/related; boundary="MIMEBoundary_a17599f2fbb7c3a49741ffca180b26ffa04943f006c90664"; + type="text/xml"; start="<0.517599f2fbb7c3a49741ffca180b26ffa04943f006c90664@apache.org>" + Date: + - Tue, 08 Jun 2021 18:44:48 GMT + Set-Cookie: + - NSC_JOaej3fgcb2frlldhm34a0bv1wqbpdp=ffffffff0949c64645525d5f4f58455e445a4a4229f2;expires=Tue, + 08-Jun-2021 18:46:48 GMT;path=/;secure;httponly + Transfer-Encoding: + - chunked + status: + code: 200 + message: OK +- request: + body: "\n\n\t\n\t\t\n\t000000\n\tpassword\ + \ \n\n\t\n\t\n\t\t\n FR\n\ + \ 69155\n VILLURB\n\ + \n\n\t\n" + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '673' + SOAPAction: + - + User-Agent: + - python-requests/2.21.0 + content-type: + - text/xml + method: POST + uri: https://espace-rct.geodis.com/geolabel/services/RechercherLocalite + response: + body: + string: "--MIMEBoundary_747599f2fbb7c3a445eb2cca180b26ffc04943f006c90664\r\n\ + Content-Type: text/xml; charset=UTF-8\r\nContent-Transfer-Encoding: binary\r\ + \nContent-ID: <0.647599f2fbb7c3a445eb2cca180b26ffc04943f006c90664@apache.org>\r\ + \n\r\nsoapenv:ServerExceptionGeoLabelMessageT0023Localit\xE9\ + \ inexistante\r\ + \n--MIMEBoundary_747599f2fbb7c3a445eb2cca180b26ffc04943f006c90664--\r\n" + headers: + Connection: + - keep-alive + Content-Length: + - '815' + Content-Type: + - multipart/related; boundary="MIMEBoundary_747599f2fbb7c3a445eb2cca180b26ffc04943f006c90664"; + type="text/xml"; start="<0.647599f2fbb7c3a445eb2cca180b26ffc04943f006c90664@apache.org>" + Date: + - Tue, 08 Jun 2021 18:44:48 GMT + Set-Cookie: + - NSC_JOaej3fgcb2frlldhm34a0bv1wqbpdp=ffffffff0949c64645525d5f4f58455e445a4a4229f2;expires=Tue, + 08-Jun-2021 18:46:48 GMT;path=/;secure;httponly + status: + code: 500 + message: Internal Server Error +version: 1 diff --git a/delivery_roulier_geodis_fr/tests/cassettes/GeodisFrLabelCase.test_labels.yaml b/delivery_roulier_geodis_fr/tests/cassettes/GeodisFrLabelCase.test_labels.yaml new file mode 100644 index 0000000000..bae981cafe --- /dev/null +++ b/delivery_roulier_geodis_fr/tests/cassettes/GeodisFrLabelCase.test_labels.yaml @@ -0,0 +1,95 @@ +interactions: +- request: + body: "\n\n\t\n\t\t\n\t000000\n\tpassword\ + \ \n\n\t\n\t\n\t\t\n 459059\n\ + \ 159\n 000000\n\ + \ Z\n 1\n\ + \ MES\n 00000001\n 20210609\n\ + \ My Company (San Francisco)\n FR\n\ + \ 69\n 69100\n VILLEURBANNE\n\ + \ +1(650)691-3277\n Carrier label test\ + \ customer\n 27 Rue Henri Rolland\n \ + \ FR\n 69100\n VILLEURBANNE\n\ + \ Carrier label test customer\n 1\n\ + \ 1.2\n \n PC\n\ + \ 1.2\n 0\n 1\n\ + \ PACK0000003\n \n\n\ + \n\t\n" + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '1643' + SOAPAction: + - + User-Agent: + - python-requests/2.21.0 + content-type: + - text/xml + method: POST + uri: https://espace-rct.geodis.com/geolabel/services/ImpressionEtiquette + response: + body: + string: "--MIMEBoundary_027599f2fbb7c3a44f903eca180b26ff924943f006c90664\r\n\ + Content-Type: text/xml; charset=UTF-8\r\nContent-Transfer-Encoding: binary\r\ + \nContent-ID: <0.327599f2fbb7c3a44f903eca180b26ff924943f006c90664@apache.org>\r\ + \n\r\ncid:127599f2fbb7c3a44f903eca180b26ff924943f006c90664@apache.orgFR69100VILLEURBANNEM20692LM2069PC1JVGTC1597870000000130PACK0000003\r\ + \n--MIMEBoundary_027599f2fbb7c3a44f903eca180b26ff924943f006c90664\r\nContent-Type:\ + \ application/octet-stream\r\nContent-Transfer-Encoding: binary\r\nContent-ID:\ + \ <127599f2fbb7c3a44f903eca180b26ff924943f006c90664@apache.org>\r\n\r\n^XA\r\ + \n^PR7,7,8^MCY^LRN^FWN^CFD,24^LH10,15^CI0^MNY^MTD^MD0^PON^PMN\r\n^LL1080\r\ + \n^FO0,0^GB830,0,3,B,0^FS\r\n^FO115,0^GB0,120,3,B,0^FS\r\n^FO320,0^GB0,120,3,B,0^FS\r\ + \n^FO576,0^GB0,120,3,B,0^FS\r\n^FO576,60^GB252,0,3,B,0^FS\r\n^FO0,120^GB830,0,3,B,0^FS\r\ + \n^FO216,120^GB0,256,3,B,0^FS\r\n^FO560,120^GB0,56,3,B,0^FS\r\n^FO0,176^GB830,0,3,B,0^FS\r\ + \n^FO0,278^GB216,0,3,B,0^FS\r\n^FO0,376^GB830,0,3,B,0^FS\r\n^FO0,416^GB830,0,1,B,0^FS\r\ + \n^FX --DEBUT IMPRESSION DONNEES-- ^FS\r\n^A0N,135,110^FO5,13^CI0^FDM^FS\r\ + \n^A0N,90,80^FO80,47^CI0^FD2^FS\r\n^A0N,110,120^FO120,30^CI0^FD0^FS\r\n^A0N,140,130^FO180,10^CI0^FD69^FS\r\ + \n^A0N,60,70^FO590,10^CI0^FD^FS\r\n^A0N,60,70^FO590,68^CI0^FD^FS\r\n^A0N,25,18^FO0,128^CI0^FD159\ + \ - FE LILLE^FS\r\n^A0N,25,18^FO0,152^CI0^FDTel: 0892052828^FS\r\n^A0N,25,18^FO224,152^CI0^FDShp:^FS\r\ + \n^A0N,45,30^FO260,136^CI0^FD00000001^FS\r\n^A0N,25,18^FO376,152^CI0^FDfrom^FS\r\ + \n^A0N,45,30^FO416,136^CI0^FD09/06/2021^FS\r\n^A0N,45,30^FO568,136^CI0^FD^FS\r\ + \n^A0N,38,20^FO0,180^CI0^FDMY COMPANY SAN FRANC^FS\r\n^A0N,38,20^FO0,214^CI0^FDFR\ + \ 69 VILLEURBANNE^FS\r\n^A0N,38,20^FO0,248^CI0^FD1 650 691-3277^FS\r\n^A0N,45,35^FO228,184^CI0^FDCARRIER\ + \ LABEL TEST CUSTOMER^FS\r\n^A0N,30,25^FO228,224^CI0^FD27 RUE HENRI ROLLAND^FS\r\ + \n^A0N,30,25^FO228,254^CI0^FD^FS\r\n^A0N,45,35^FO228,284^CI0^FDFR 69100 VILLEURBANNE^FS\r\ + \n^A0N,45,35^FO228,336^CI0^FDCARRIER LABEL TEST CUSTOMER^FS\r\n^A0N,25,18^FO0,288^CI0^FDUM^FS\r\ + \n^A0N,25,18^FO0,320^CI0^FDWGHT^FS\r\n^A0N,25,18^FO0,352^CI0^FDVOL^FS\r\n\ + ^A0N,37,25^FO80,284^CI0^FD1/1^FS\r\n^A0N,37,25^FO80,316^CI0^FD1.2/1.2^FS\r\ + \n^A0N,37,25^FO80,348^CI0^FD-/-^FS\r\n^A0N,25,18^FO0,388^CI0^FDRef2 clt :^FS\r\ + \n^A0N,30,25^FO90,384^CI0^FD^FS\r\n^A0N,25,18^FO0,428^CI0^FDRef colis :^FS\r\ + \n^A0N,30,25^FO90,424^CI0^FDPACK0000003^FS\r\n^FO0,456^GB830,0,1,B,0^FS\r\n\ + ^BY3^FO49,732^BCN,180,N,N,N,A^FN1^FS\r\n^A0N,24,24^FO138,922^CI0^FN2^FS\r\n\ + ^FN1^FDJVGTC1597870000000130^FS\r\n^FN2^FDJVGTC1597870000000130^FS\r\n^BY3^FO280,470^BCN,180,N,N,N,A^FN3^FS\r\ + \n^A0N,24,24^FO420,655^CI0^FN4^FS\r\n^FN3^FD2LM2069^FS\r\n^FN4^FD2LM2069^FS\r\ + \n^A0N,35,35^FO640,980^CI0^FD^FS\r\n^FO0,1000^GB840,0,2,B,0^FS\r\n^FO0,1037^GB840,0,2,B,0^FS\r\ + \n^A0N,32,28^FO5,1006^CI0^FD^FS\r\n^FX --FIN IMPRESSION DONNEES-- ^FS\r\n\ + ^PQ1,0,1,Y\r\n^XZ\r\n\r\n--MIMEBoundary_027599f2fbb7c3a44f903eca180b26ff924943f006c90664--\r\ + \n" + headers: + Connection: + - keep-alive + Content-Type: + - multipart/related; boundary="MIMEBoundary_027599f2fbb7c3a44f903eca180b26ff924943f006c90664"; + type="text/xml"; start="<0.327599f2fbb7c3a44f903eca180b26ff924943f006c90664@apache.org>" + Date: + - Tue, 08 Jun 2021 17:07:18 GMT + Set-Cookie: + - NSC_JOaej3fgcb2frlldhm34a0bv1wqbpdp=ffffffff0949c64645525d5f4f58455e445a4a4229f2;expires=Tue, + 08-Jun-2021 17:09:18 GMT;path=/;secure;httponly + Transfer-Encoding: + - chunked + status: + code: 200 + message: OK +version: 1 diff --git a/delivery_roulier_geodis_fr/tests/test_geodis_labels.py b/delivery_roulier_geodis_fr/tests/test_geodis_labels.py new file mode 100644 index 0000000000..73f8d9a360 --- /dev/null +++ b/delivery_roulier_geodis_fr/tests/test_geodis_labels.py @@ -0,0 +1,117 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from vcr_unittest import VCRMixin + +from odoo.addons.base_delivery_carrier_label.tests import carrier_label_case + + +class GeodisFrLabelCase(VCRMixin, carrier_label_case.CarrierLabelCase): + def setUp(self, *args, **kwargs): + # need it to be defined before super to avoid failure in _hide_sensitive_data + self.account = False + super().setUp(*args, **kwargs) + # french carrier sender need to be from France + self.picking.company_id.partner_id.write( + { + "country_id": self.env.ref("base.fr").id, + "city": "VILLEURBANNE", + "zip": "69100", + } + ) + self.account = self.env["carrier.account"].create( + { + "name": "Geodis - France Express", + "delivery_type": "geodis_fr", + # fill real account information if you want to re-generate cassette + "account": "45393e38323033372b3c3334", + "password": "2d5a44584356", + "geodis_fr_customer_id": "787000", + } + ) + self.agency = self.env["delivery.carrier.agency"].create( + { + "name": "Lille Agency", + "external_reference": "459059", + "delivery_type": "geodis_fr", + "partner_id": self.env.ref( + "delivery_roulier_geodis_fr.default_geodis_france_exress_agency_partner" + ).id, + "geodis_fr_interchange_sender": "222222222", + "geodis_fr_interchange_recipient": "111111111", + "geodis_fr_hub_id": "159", + } + ) + + def _hide_sensitive_data(self, request): + password = self.account and self.account.password or "dummy" + account = self.account and self.account.account or "dummy" + customer_id = self.account and self.account.geodis_fr_customer_id or "dummy" + body = request.body + body = body.replace(password.encode(), b"password") + body = body.replace(account.encode(), b"000000") + body = body.replace(customer_id.encode(), b"000000") + request.body = body + return request + + def _get_vcr_kwargs(self, **kwargs): + return { + "record_mode": "once", + "match_on": ["method", "path"], + "decode_compressed_response": True, + "before_record_request": self._hide_sensitive_data, + } + + def _product_data(self): + data = super()._product_data() + data.update( + { + "weight": 1.2, + } + ) + return data + + def _create_order_picking(self): + super()._create_order_picking() + + def _get_carrier(self): + return self.env.ref("delivery_roulier_geodis_fr.delivery_carrier_mes") + + def _partner_data(self): + data = super()._partner_data() + data.update( + { + "street": "27 Rue Henri Rolland", + "zip": "69100", + "city": "VILLEURBANNE", + "country_id": self.env.ref("base.fr").id, + } + ) + return data + + def test_labels(self): + super().test_labels() + self.assertTrue(self.picking.geodis_shippingid) + + def test_edi(self): + deposit = self.env["deposit.slip"].create( + { + "name": "test", + "delivery_type": "geodis_fr", + "picking_ids": [(6, 0, self.picking.ids)], + } + ) + # filled on label generation / simulate it + self.picking.write({"geodis_shippingid": "0123456"}) + deposit.validate_deposit() + attachment = self.env["ir.attachment"].search( + [("res_id", "=", deposit.id), ("res_model", "=", "deposit.slip")] + ) + self.assertEqual(len(attachment), 1) + self.assertTrue(attachment.datas) + + def test_addresses(self): + addresses = self.picking._geodis_fr_get_address_proposition() + self.assertEqual(len(addresses), 1) + self.picking.partner_id.write({"zip": 69155, "city": "VILLURB"}) + addresses = self.picking._geodis_fr_get_address_proposition(raise_address=False) + self.assertEqual(len(addresses), 0) diff --git a/delivery_roulier_geodis_fr/views/carrier_account_views.xml b/delivery_roulier_geodis_fr/views/carrier_account_views.xml new file mode 100644 index 0000000000..6c97fd06d2 --- /dev/null +++ b/delivery_roulier_geodis_fr/views/carrier_account_views.xml @@ -0,0 +1,28 @@ + + + + + carrier.account + + + + + + + + + + + diff --git a/delivery_roulier_geodis_fr/views/delivery_carrier_agency_views.xml b/delivery_roulier_geodis_fr/views/delivery_carrier_agency_views.xml new file mode 100644 index 0000000000..3050927ef9 --- /dev/null +++ b/delivery_roulier_geodis_fr/views/delivery_carrier_agency_views.xml @@ -0,0 +1,28 @@ + + + + + delivery.carrier.agency + + + + + + + + + + + From b28e42962558020a66057e84092e8c48d59631a4 Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Fri, 3 Sep 2021 18:11:15 +0200 Subject: [PATCH 03/13] Fix notification for geodis M means mail AND sms while P means email --- delivery_roulier_geodis_fr/models/stock_picking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/delivery_roulier_geodis_fr/models/stock_picking.py b/delivery_roulier_geodis_fr/models/stock_picking.py index ac4a41ce4c..8f159ecae2 100644 --- a/delivery_roulier_geodis_fr/models/stock_picking.py +++ b/delivery_roulier_geodis_fr/models/stock_picking.py @@ -68,9 +68,9 @@ def _geodis_fr_get_notifications(self, package): if "RDW" in options: if recipient["email"]: if recipient["phone"]: - return "P" - else: return "M" + else: + return "P" else: if recipient["phone"]: return "S" From 1ad6ca429b36d5b4b13e19215cef686659376620 Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Fri, 30 Sep 2022 16:07:08 +0200 Subject: [PATCH 04/13] [FIX] Make sure tracking account is not choosen for labels --- delivery_roulier_geodis_fr/models/stock_picking.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/delivery_roulier_geodis_fr/models/stock_picking.py b/delivery_roulier_geodis_fr/models/stock_picking.py index 8f159ecae2..21caec9462 100644 --- a/delivery_roulier_geodis_fr/models/stock_picking.py +++ b/delivery_roulier_geodis_fr/models/stock_picking.py @@ -223,3 +223,8 @@ def _geodis_fr_get_account_tracking(self): limit=1, ) return account + + def _get_carrier_account_domain(self): + domain = super()._get_carrier_account_domain() + domain.append(("geodis_fr_tracking_account", "!=", True)) + return domain From efdc6e2e094b2e439631062d45f30f24d15c9fbc Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Thu, 24 Nov 2022 12:30:00 +0100 Subject: [PATCH 05/13] [IMP] delivery_roulier_geodis_fr: black, isort, prettier --- delivery_roulier_geodis_fr/models/deposit.py | 2 +- .../odoo/addons/delivery_roulier_geodis_fr | 1 + setup/delivery_roulier_geodis_fr/setup.py | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 120000 setup/delivery_roulier_geodis_fr/odoo/addons/delivery_roulier_geodis_fr create mode 100644 setup/delivery_roulier_geodis_fr/setup.py diff --git a/delivery_roulier_geodis_fr/models/deposit.py b/delivery_roulier_geodis_fr/models/deposit.py index 50a845883c..f6f66f654a 100644 --- a/delivery_roulier_geodis_fr/models/deposit.py +++ b/delivery_roulier_geodis_fr/models/deposit.py @@ -116,7 +116,7 @@ def _geodis_create_edi_file(self, payload): try: edi = roulier.get(self.delivery_type, "get_edi", payload) except InvalidApiInput as e: - raise UserError(_(u"Bad input: %s\n" % str(e))) + raise UserError(_("Bad input: %s\n" % str(e))) return edi def _get_geodis_attachment_name(self, idx, payload_agency): diff --git a/setup/delivery_roulier_geodis_fr/odoo/addons/delivery_roulier_geodis_fr b/setup/delivery_roulier_geodis_fr/odoo/addons/delivery_roulier_geodis_fr new file mode 120000 index 0000000000..2a2e878ffc --- /dev/null +++ b/setup/delivery_roulier_geodis_fr/odoo/addons/delivery_roulier_geodis_fr @@ -0,0 +1 @@ +../../../../delivery_roulier_geodis_fr \ No newline at end of file diff --git a/setup/delivery_roulier_geodis_fr/setup.py b/setup/delivery_roulier_geodis_fr/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/delivery_roulier_geodis_fr/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From cb7e7ebca30449b1ad852ab3a8c668c09c27bdf9 Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Thu, 24 Nov 2022 12:39:18 +0100 Subject: [PATCH 06/13] [MIG][16] Migrate delivery_roulier_geodis_fr to v16 --- delivery_roulier_geodis_fr/__manifest__.py | 3 ++- delivery_roulier_geodis_fr/models/carrier_account.py | 2 +- delivery_roulier_geodis_fr/models/deposit.py | 2 +- delivery_roulier_geodis_fr/models/stock_picking.py | 4 ++-- delivery_roulier_geodis_fr/tests/test_geodis_labels.py | 7 ++++--- delivery_roulier_geodis_fr/views/carrier_account_views.xml | 2 +- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/delivery_roulier_geodis_fr/__manifest__.py b/delivery_roulier_geodis_fr/__manifest__.py index 57975a12ac..52e8b97ccc 100644 --- a/delivery_roulier_geodis_fr/__manifest__.py +++ b/delivery_roulier_geodis_fr/__manifest__.py @@ -2,7 +2,7 @@ { "name": "Delivery Carrier Geodis (fr)", - "version": "14.0.1.0.0", + "version": "16.0.1.0.0", "author": "Akretion, Odoo Community Association (OCA)", "summary": "Generate Label for Geodis logistic", "maintainer": "Akretion, Odoo Community Association (OCA)", @@ -12,6 +12,7 @@ "delivery_carrier_agency", "delivery_carrier_deposit", "delivery_roulier_option", + "partner_helper", "l10n_fr_siret", ], "website": "https://github.com/OCA/delivery-carrier", diff --git a/delivery_roulier_geodis_fr/models/carrier_account.py b/delivery_roulier_geodis_fr/models/carrier_account.py index 1b45cc2992..51301b5b00 100644 --- a/delivery_roulier_geodis_fr/models/carrier_account.py +++ b/delivery_roulier_geodis_fr/models/carrier_account.py @@ -8,7 +8,7 @@ class CarrierAccount(models.Model): geodis_fr_customer_id = fields.Char(string="Customer Id") geodis_fr_file_format = fields.Selection( - [("ZPL", "ZPL")], default="ZPL", string="File Format" + [("ZPL", "ZPL")], default="ZPL", string="Geodis File Format" ) geodis_fr_tracking_account = fields.Boolean( string="Is a Tracking Account", diff --git a/delivery_roulier_geodis_fr/models/deposit.py b/delivery_roulier_geodis_fr/models/deposit.py index f6f66f654a..a3507e9b3f 100644 --- a/delivery_roulier_geodis_fr/models/deposit.py +++ b/delivery_roulier_geodis_fr/models/deposit.py @@ -116,7 +116,7 @@ def _geodis_create_edi_file(self, payload): try: edi = roulier.get(self.delivery_type, "get_edi", payload) except InvalidApiInput as e: - raise UserError(_("Bad input: %s\n" % str(e))) + raise UserError(_("Bad input: %s\n") % str(e)) from e return edi def _get_geodis_attachment_name(self, idx, payload_agency): diff --git a/delivery_roulier_geodis_fr/models/stock_picking.py b/delivery_roulier_geodis_fr/models/stock_picking.py index 21caec9462..86a5037025 100644 --- a/delivery_roulier_geodis_fr/models/stock_picking.py +++ b/delivery_roulier_geodis_fr/models/stock_picking.py @@ -131,7 +131,7 @@ def _geodis_fr_get_address_proposition(self, raise_address=True): except InvalidApiInput as e: raise UserError( self.env["stock.quant.package"]._invalid_api_input_handling(payload, e) - ) + ) from e except CarrierError as e: errors = e.args and e.args[0] if ( @@ -144,7 +144,7 @@ def _geodis_fr_get_address_proposition(self, raise_address=True): else: package = self.env["stock.quant.package"].new({}) package.carrier_id = self.carrier_id - raise UserError(package._carrier_error_handling(payload, e)) + raise UserError(package._carrier_error_handling(payload, e)) from e return addresses def _geodis_fr_check_address(self): diff --git a/delivery_roulier_geodis_fr/tests/test_geodis_labels.py b/delivery_roulier_geodis_fr/tests/test_geodis_labels.py index 73f8d9a360..67541b61b0 100644 --- a/delivery_roulier_geodis_fr/tests/test_geodis_labels.py +++ b/delivery_roulier_geodis_fr/tests/test_geodis_labels.py @@ -5,7 +5,7 @@ from odoo.addons.base_delivery_carrier_label.tests import carrier_label_case -class GeodisFrLabelCase(VCRMixin, carrier_label_case.CarrierLabelCase): +class GeodisFrLabelCase(VCRMixin, carrier_label_case.TestCarrierLabel): def setUp(self, *args, **kwargs): # need it to be defined before super to avoid failure in _hide_sensitive_data self.account = False @@ -71,7 +71,7 @@ def _product_data(self): return data def _create_order_picking(self): - super()._create_order_picking() + return super()._create_order_picking() def _get_carrier(self): return self.env.ref("delivery_roulier_geodis_fr.delivery_carrier_mes") @@ -89,8 +89,9 @@ def _partner_data(self): return data def test_labels(self): - super().test_labels() + res = super().test_labels() self.assertTrue(self.picking.geodis_shippingid) + return res def test_edi(self): deposit = self.env["deposit.slip"].create( diff --git a/delivery_roulier_geodis_fr/views/carrier_account_views.xml b/delivery_roulier_geodis_fr/views/carrier_account_views.xml index 6c97fd06d2..50a5c55398 100644 --- a/delivery_roulier_geodis_fr/views/carrier_account_views.xml +++ b/delivery_roulier_geodis_fr/views/carrier_account_views.xml @@ -5,7 +5,7 @@ carrier.account From 05114ec3c465e1d249a7d7ebfae5826c1558b886 Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Wed, 24 May 2023 11:58:57 +0200 Subject: [PATCH 07/13] [IMP] delivery_roulier_geodis_fr : Update tracking ref on picking --- delivery_roulier_geodis_fr/models/stock_picking.py | 1 + delivery_roulier_geodis_fr/models/stock_quant_package.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/delivery_roulier_geodis_fr/models/stock_picking.py b/delivery_roulier_geodis_fr/models/stock_picking.py index 86a5037025..4a23499164 100644 --- a/delivery_roulier_geodis_fr/models/stock_picking.py +++ b/delivery_roulier_geodis_fr/models/stock_picking.py @@ -187,6 +187,7 @@ def _geodis_fr_update_tracking(self): continue # multipack not implemented yet data = ret[0] + rec.write({"carrier_tracking_ref": data["tracking"]["trackingCode"]}) packages.write( { "parcel_tracking_uri": data["tracking"]["publicUrl"], diff --git a/delivery_roulier_geodis_fr/models/stock_quant_package.py b/delivery_roulier_geodis_fr/models/stock_quant_package.py index f07dc01077..57b38d2e6d 100644 --- a/delivery_roulier_geodis_fr/models/stock_quant_package.py +++ b/delivery_roulier_geodis_fr/models/stock_quant_package.py @@ -23,6 +23,14 @@ def _geodis_fr_parse_response(self, picking, response): } ) i += 1 + # add geodis_shipping_id in res so it is written on picking. + # it is not really a tracking number, it will actually be used to get + # the tracking once the edi file is sent. + # we prefer filling because the parcel tracking ref is used to display the + # label generation button, cancel label button, etc + res["tracking_number"] = ( + not res.get("tracking_number") and picking.geodis_shippingid or "" + ) return res def _geodis_fr_should_include_customs(self, picking): From 9bc4aa4ec27c35a3305555905cb1a7552ddb98ac Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Tue, 6 Jun 2023 11:11:51 +0200 Subject: [PATCH 08/13] [FIX] delivery_roulier_geodis_fr : Fix notification when no email and phone --- delivery_roulier_geodis_fr/models/stock_picking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/delivery_roulier_geodis_fr/models/stock_picking.py b/delivery_roulier_geodis_fr/models/stock_picking.py index 4a23499164..ae64585283 100644 --- a/delivery_roulier_geodis_fr/models/stock_picking.py +++ b/delivery_roulier_geodis_fr/models/stock_picking.py @@ -66,13 +66,13 @@ def _geodis_fr_get_notifications(self, package): options = self._get_options(package) recipient = self._convert_address(self._get_receiver(package)) if "RDW" in options: - if recipient["email"]: + if recipient.get("email"): if recipient["phone"]: return "M" else: return "P" else: - if recipient["phone"]: + if recipient.get("phone"): return "S" else: raise UserError(_("Can't set up a rendez-vous wihout mail or tel")) From 1262db1ef92c78b9be435962aa5bcca46e4ebb90 Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Thu, 6 Jul 2023 23:13:41 +0200 Subject: [PATCH 09/13] [FIX] delivery_roulier_geodis_fr : change dependency of renamed module --- delivery_roulier_geodis_fr/__manifest__.py | 2 +- delivery_roulier_geodis_fr/models/stock_picking.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/delivery_roulier_geodis_fr/__manifest__.py b/delivery_roulier_geodis_fr/__manifest__.py index 52e8b97ccc..80011cd4cd 100644 --- a/delivery_roulier_geodis_fr/__manifest__.py +++ b/delivery_roulier_geodis_fr/__manifest__.py @@ -12,7 +12,7 @@ "delivery_carrier_agency", "delivery_carrier_deposit", "delivery_roulier_option", - "partner_helper", + "partner_address_split", "l10n_fr_siret", ], "website": "https://github.com/OCA/delivery-carrier", diff --git a/delivery_roulier_geodis_fr/models/stock_picking.py b/delivery_roulier_geodis_fr/models/stock_picking.py index ae64585283..04c1217bb5 100644 --- a/delivery_roulier_geodis_fr/models/stock_picking.py +++ b/delivery_roulier_geodis_fr/models/stock_picking.py @@ -39,7 +39,7 @@ class StockPicking(models.Model): def _geodis_fr_convert_address(self, partner): """Truncate address and name to 35 chars.""" address = self._roulier_convert_address(partner) or {} - # get_split_adress from partner_helper module + # get_split_adress from partner_address_split module streets = partner._get_split_address(3, 35) address["street1"], address["street2"], address["street3"] = streets for field in ("name", "city"): @@ -183,7 +183,7 @@ def _geodis_fr_update_tracking(self): ret = roulier.get(rec.delivery_type, "get_tracking_list", payload) if len(ret) != 1: - _logger.warning("Geodis tracking not found. Picking %s" % rec.id) + _logger.info("Geodis tracking not found. Picking %s" % rec.id) continue # multipack not implemented yet data = ret[0] From 1a214c3037794e33b8edf25323390437002b8a14 Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Mon, 4 Dec 2023 11:43:21 +0100 Subject: [PATCH 10/13] [FIX] Do not copy geodis_shippingid as we dont want to send the same to geodis in case of return and new shipping --- delivery_roulier_geodis_fr/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/delivery_roulier_geodis_fr/models/stock_picking.py b/delivery_roulier_geodis_fr/models/stock_picking.py index 04c1217bb5..1417263983 100644 --- a/delivery_roulier_geodis_fr/models/stock_picking.py +++ b/delivery_roulier_geodis_fr/models/stock_picking.py @@ -34,7 +34,7 @@ class StockPicking(models.Model): _inherit = "stock.picking" - geodis_shippingid = fields.Char(help="Shipping Id in Geodis terminology") + geodis_shippingid = fields.Char(help="Shipping Id in Geodis terminology", copy=False) def _geodis_fr_convert_address(self, partner): """Truncate address and name to 35 chars.""" From 0d884a652b28f79bf01d5f9f1f6a58a7e25c23e4 Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Tue, 23 Jul 2024 18:46:02 +0200 Subject: [PATCH 11/13] [IMP] add hook to get package vals for EDI --- delivery_roulier_geodis_fr/models/stock_picking.py | 9 ++++----- delivery_roulier_geodis_fr/models/stock_quant_package.py | 7 +++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/delivery_roulier_geodis_fr/models/stock_picking.py b/delivery_roulier_geodis_fr/models/stock_picking.py index 1417263983..0c548380a5 100644 --- a/delivery_roulier_geodis_fr/models/stock_picking.py +++ b/delivery_roulier_geodis_fr/models/stock_picking.py @@ -34,7 +34,9 @@ class StockPicking(models.Model): _inherit = "stock.picking" - geodis_shippingid = fields.Char(help="Shipping Id in Geodis terminology", copy=False) + geodis_shippingid = fields.Char( + help="Shipping Id in Geodis terminology", copy=False + ) def _geodis_fr_convert_address(self, partner): """Truncate address and name to 35 chars.""" @@ -96,10 +98,7 @@ def _geodis_fr_prepare_edi(self): picking = self packages = picking.package_ids - parcels = [ - {"barcode": pack.geodis_cab, "weight": pack.shipping_weight or pack.weight} - for pack in packages - ] + parcels = [pack._get_edi_pack_vals() for pack in packages] return { "product": picking.carrier_id.code, diff --git a/delivery_roulier_geodis_fr/models/stock_quant_package.py b/delivery_roulier_geodis_fr/models/stock_quant_package.py index 57b38d2e6d..f21146cc21 100644 --- a/delivery_roulier_geodis_fr/models/stock_quant_package.py +++ b/delivery_roulier_geodis_fr/models/stock_quant_package.py @@ -39,3 +39,10 @@ def _geodis_fr_should_include_customs(self, picking): def _geodis_fr_get_tracking_link(self): return self.parcel_tracking_uri + + def _get_edi_pack_vals(self): + self.ensure_one() + return { + "barcode": self.geodis_cab, + "weight": self.shipping_weight or self.weight, + } From f17416d3e51271503f617f7cdbf532b578186440 Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Wed, 30 Oct 2024 17:17:05 +0100 Subject: [PATCH 12/13] [IMP] delivery_roulier_geodis_fr : fix tests + add maintainer --- delivery_roulier_geodis_fr/__manifest__.py | 2 +- .../tests/test_geodis_labels.py | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/delivery_roulier_geodis_fr/__manifest__.py b/delivery_roulier_geodis_fr/__manifest__.py index 80011cd4cd..5d2dc42bd6 100644 --- a/delivery_roulier_geodis_fr/__manifest__.py +++ b/delivery_roulier_geodis_fr/__manifest__.py @@ -5,7 +5,7 @@ "version": "16.0.1.0.0", "author": "Akretion, Odoo Community Association (OCA)", "summary": "Generate Label for Geodis logistic", - "maintainer": "Akretion, Odoo Community Association (OCA)", + "maintainers": ["florian-dacosta"], "category": "Warehouse", "depends": [ "delivery_roulier", diff --git a/delivery_roulier_geodis_fr/tests/test_geodis_labels.py b/delivery_roulier_geodis_fr/tests/test_geodis_labels.py index 67541b61b0..ef818dd5cd 100644 --- a/delivery_roulier_geodis_fr/tests/test_geodis_labels.py +++ b/delivery_roulier_geodis_fr/tests/test_geodis_labels.py @@ -28,6 +28,8 @@ def setUp(self, *args, **kwargs): "geodis_fr_customer_id": "787000", } ) + carrier = self.env.ref("delivery_roulier_geodis_fr.delivery_carrier_mes") + carrier.carrier_account_id = self.account.id self.agency = self.env["delivery.carrier.agency"].create( { "name": "Lille Agency", @@ -61,6 +63,13 @@ def _get_vcr_kwargs(self, **kwargs): "before_record_request": self._hide_sensitive_data, } + def _transfer_order_picking(self): + for move in self.picking.move_ids: + move.quantity_done = move.product_uom_qty + move_lines = self.picking.move_line_ids + self.picking._put_in_pack(move_lines) + return super()._transfer_order_picking() + def _product_data(self): data = super()._product_data() data.update( @@ -88,12 +97,9 @@ def _partner_data(self): ) return data - def test_labels(self): + def test_labels_and_edi(self): res = super().test_labels() self.assertTrue(self.picking.geodis_shippingid) - return res - - def test_edi(self): deposit = self.env["deposit.slip"].create( { "name": "test", @@ -101,14 +107,13 @@ def test_edi(self): "picking_ids": [(6, 0, self.picking.ids)], } ) - # filled on label generation / simulate it - self.picking.write({"geodis_shippingid": "0123456"}) deposit.validate_deposit() attachment = self.env["ir.attachment"].search( [("res_id", "=", deposit.id), ("res_model", "=", "deposit.slip")] ) self.assertEqual(len(attachment), 1) self.assertTrue(attachment.datas) + return res def test_addresses(self): addresses = self.picking._geodis_fr_get_address_proposition() From ec12ac6da82eafa65a07b1afd28dd442eca5a0fd Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Wed, 12 Mar 2025 18:30:52 +0100 Subject: [PATCH 13/13] [FIX] Test compatibility of delivery_package_fee when no CoA is loaded on main company --- delivery_package_fee/tests/test_package_fee.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/delivery_package_fee/tests/test_package_fee.py b/delivery_package_fee/tests/test_package_fee.py index e0e7df50d6..fb0f9d4a6d 100644 --- a/delivery_package_fee/tests/test_package_fee.py +++ b/delivery_package_fee/tests/test_package_fee.py @@ -154,6 +154,16 @@ def test_package_fee_simple_with_fiscal_position_tax(self): "include_base_amount": True, } ) + tax_price_include2 = self.env["account.tax"].create( + { + "name": "15% inc", + "type_tax_use": "sale", + "amount_type": "percent", + "amount": 15, + "price_include": True, + "include_base_amount": True, + } + ) tax_price_exclude = self.env["account.tax"].create( { "name": "15% exc", @@ -180,6 +190,7 @@ def test_package_fee_simple_with_fiscal_position_tax(self): ) # Setting tax in fiscal position on fee2 product + self.fee1.taxes_id = tax_price_include2 self.fee2.taxes_id = tax_price_include self.sale.fiscal_position_id = fiscal_position