Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[16.0][MIG] delivery_schenker #632

Merged
merged 23 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
9b581bf
[ADD] delivery_schenker: New module
chienandalu Aug 20, 2021
fa4fd28
[UPD] Update delivery_schenker.pot
oca-travis Dec 11, 2021
57b6fc0
[UPD] README.rst
OCA-git-bot Dec 11, 2021
cc46f5c
[FIX] delivery_schenker: undeclared method
chienandalu Dec 14, 2021
e83ac02
[UPD] Update delivery_schenker.pot
oca-travis Dec 14, 2021
a736766
delivery_schenker 13.0.1.0.1
OCA-git-bot Dec 14, 2021
f6e0f59
[IMP] delivery_schenker: black, isort, prettier
hildickethan Dec 13, 2021
5d26505
[MIG] delivery_schenker: Migration to 14.0
hildickethan Dec 13, 2021
dbec813
[IMP] delivery_schenker: Minimum for volume
hildickethan Dec 14, 2021
7189f77
[IMP] delivery_schenker: Add tracking
hildickethan Dec 14, 2021
36d4215
[IMP] delivery_schenker: Print extra label button
hildickethan Dec 14, 2021
995d8f0
[I18N] delivery_schenker: Spanish translation
hildickethan Dec 14, 2021
ebf669e
[UPD] Update delivery_schenker.pot
oca-travis Dec 14, 2021
64e62fa
[UPD] README.rst
OCA-git-bot Dec 14, 2021
9ee0d8e
[14.0][IMP] delivery_schenker: Return expected dict instead of error
hildickethan Jan 13, 2022
39fc1fa
[UPD] Update delivery_schenker.pot
Oct 17, 2022
b69990b
delivery_schenker 14.0.1.0.1
OCA-git-bot Oct 17, 2022
acdc1e5
Update translation files
weblate Oct 17, 2022
aa7e5f8
[UPD] Update delivery_schenker.pot
Nov 3, 2022
58af1a8
Update translation files
weblate Nov 3, 2022
21f9b1e
[FIX] delivery_schenker: fix missing dependency, before there was no …
mt-software-de Mar 24, 2023
5264884
[IMP] delivery_schenker: black, isort, prettier
ferran-S73 Apr 18, 2023
0e0fa7e
[16.0][MIG] delivery_schenker
ferran-S73 Apr 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion delivery_schenker/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Delivery Schenker
:target: https://runboat.odoo-community.org/webui/builds.html?repo=OCA/delivery-carrier&target_branch=14.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|
|badge1| |badge2| |badge3| |badge4| |badge5|

This module links the `DB Schenker <https://www.dbschenker.com>`_ booking and tracking
APIs with Odoo delivery system.
Expand Down Expand Up @@ -156,6 +156,7 @@ Contributors
* `Studio73 <https://www.studio73.es>`_:

* Ethan Hildick
* Ferran Mora

* Michael Tietz (MT Software) <mtietz@mt-software.de>

Expand Down
3 changes: 2 additions & 1 deletion delivery_schenker/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# Copyright 2021 Tecnativa - David Vidal
# Copyright 2021 Studio73 - Ethan Hildick <ethan@studio73.es>
# Copyright 2023 Studio73 - Ferran Mora <ferran@studio73.es>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Delivery Schenker",
"summary": "Delivery Carrier implementation for DB Schenker API",
"version": "14.0.1.0.1",
"version": "16.0.1.0.0",
"category": "Stock",
"website": "https://github.com/OCA/delivery-carrier",
"author": "Tecnativa, Studio73, Odoo Community Association (OCA)",
Expand Down
46 changes: 23 additions & 23 deletions delivery_schenker/data/delivery_schenker_data.xml
Original file line number Diff line number Diff line change
@@ -1,117 +1,117 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<!-- Schenker Product Packagings. It'd be nice to have proper measures -->
<record id="schenker_packaging_01" model="product.packaging">
<record id="schenker_package_type_01" model="stock.package.type">
<field name="shipper_package_code">CI</field>
<field name="name">(Schenker) Canister</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_02" model="product.packaging">
<record id="schenker_package_type_02" model="stock.package.type">
<field name="shipper_package_code">CT</field>
<field name="name">(Schenker) Carton</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_03" model="product.packaging">
<record id="schenker_package_type_03" model="stock.package.type">
<field name="shipper_package_code">CS</field>
<field name="name">(Schenker) Case</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_04" model="product.packaging">
<record id="schenker_package_type_04" model="stock.package.type">
<field name="shipper_package_code">CO</field>
<field name="name">(Schenker) Colli</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_05" model="product.packaging">
<record id="schenker_package_type_05" model="stock.package.type">
<field name="shipper_package_code">CH</field>
<field name="name">(Schenker) Crate</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_06" model="product.packaging">
<record id="schenker_package_type_06" model="stock.package.type">
<field name="shipper_package_code">GP</field>
<field name="name">(Schenker) Skeleton box pallet</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_07" model="product.packaging">
<record id="schenker_package_type_07" model="stock.package.type">
<field name="shipper_package_code">NE</field>
<field name="name">(Schenker) Unpacked Skid</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_08" model="product.packaging">
<record id="schenker_package_type_08" model="stock.package.type">
<field name="shipper_package_code">BG</field>
<field name="name">(Schenker) Bag</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_09" model="product.packaging">
<record id="schenker_package_type_09" model="stock.package.type">
<field name="shipper_package_code">BL</field>
<field name="name">(Schenker) Bale</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_10" model="product.packaging">
<record id="schenker_package_type_10" model="stock.package.type">
<field name="shipper_package_code">DR</field>
<field name="name">(Schenker) Barrel</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_11" model="product.packaging">
<record id="schenker_package_type_11" model="stock.package.type">
<field name="shipper_package_code">BX</field>
<field name="name">(Schenker) Box</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_12" model="product.packaging">
<record id="schenker_package_type_12" model="stock.package.type">
<field name="shipper_package_code">BY</field>
<field name="name">(Schenker) Bundle</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_13" model="product.packaging">
<record id="schenker_package_type_13" model="stock.package.type">
<field name="shipper_package_code">TR</field>
<field name="name">(Schenker) Drum</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_14" model="product.packaging">
<record id="schenker_package_type_14" model="stock.package.type">
<field name="shipper_package_code">EP</field>
<field name="name">(Schenker) Europallet</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_15" model="product.packaging">
<record id="schenker_package_type_15" model="stock.package.type">
<field name="shipper_package_code">FR</field>
<field name="name">(Schenker) Frame</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_16" model="product.packaging">
<record id="schenker_package_type_16" model="stock.package.type">
<field name="shipper_package_code">HO</field>
<field name="name">(Schenker) Hobbock</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_17" model="product.packaging">
<record id="schenker_package_type_17" model="stock.package.type">
<field name="shipper_package_code">OP</field>
<field name="name">(Schenker) One-way pallet</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_18" model="product.packaging">
<record id="schenker_package_type_18" model="stock.package.type">
<field name="shipper_package_code">PK</field>
<field name="name">(Schenker) Package</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_19" model="product.packaging">
<record id="schenker_package_type_19" model="stock.package.type">
<field name="shipper_package_code">XP</field>
<field name="name">(Schenker) Pallet</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_20" model="product.packaging">
<record id="schenker_package_type_20" model="stock.package.type">
<field name="shipper_package_code">PZ</field>
<field name="name">(Schenker) Pipe</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_21" model="product.packaging">
<record id="schenker_package_type_21" model="stock.package.type">
<field name="shipper_package_code">RO</field>
<field name="name">(Schenker) Roll</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_22" model="product.packaging">
<record id="schenker_package_type_22" model="stock.package.type">
<field name="shipper_package_code">SK</field>
<field name="name">(Schenker) Sack</field>
<field name="package_carrier_type">schenker</field>
</record>
<record id="schenker_packaging_23" model="product.packaging">
<record id="schenker_package_type_23" model="stock.package.type">
<field name="shipper_package_code">ZZ</field>
<field name="name">(Schenker) Other</field>
<field name="package_carrier_type">schenker</field>
Expand Down
23 changes: 23 additions & 0 deletions delivery_schenker/migrations/16.0.1.0.0/post-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2023 Studio73 - Ferran Mora
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from openupgradelib import openupgrade


@openupgrade.migrate()
def migrate(env, version):
for i in range(1, 23):
xml_id = "delivery_schenker.schenker_packaging_{}".format(str(i).zfill(2))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this migration script working as expected? I would have thought this XML-ID doesn't exist anymore in post migration, unless such references are dropped right after the full module upgrade?

pack = env.ref(xml_id, raise_if_not_found=False)
if pack:
xml_id = "delivery_schenker.schenker_package_type_{}".format(
str(i).zfill(2)
)
package_type = env.ref(xml_id, raise_if_not_found=False)
if package_type:
env.cr.execute(
"SELECT schenker_stackable FROM product_packaging WHERE id=%s",
(pack.id,),
)
schenker_stackable = env.cr.fetchone()
package_type.write({"schenker_stackable": schenker_stackable})
2 changes: 1 addition & 1 deletion delivery_schenker/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from . import delivery_carrier
from . import product_packaging
from . import schenker_request
from . import stock_package_type
from . import stock_picking
20 changes: 10 additions & 10 deletions delivery_schenker/models/delivery_carrier.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,66 +158,66 @@
],
default="VOLUME",
)
schenker_default_packaging_id = fields.Many2one(
comodel_name="product.packaging",
schenker_default_package_type_id = fields.Many2one(
comodel_name="stock.package.type",
string="Default Package Type",
domain=[("package_carrier_type", "=", "schenker")],
help="If not delivery package or the package doesn't have defined the packaging"
"it will default to this type",
" it will default to this type",
)

def _get_schenker_credentials(self):
"""Access key is mandatory for every request while group and user are
optional"""
credentials = {

Check warning on line 172 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L172

Added line #L172 was not covered by tests
"prod": self.prod_environment,
"access_key": self.schenker_access_key,
}
if self.schenker_group_id:
credentials["group_id"] = self.schenker_group_id

Check warning on line 177 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L177

Added line #L177 was not covered by tests
if self.schenker_user:
credentials["user"] = self.schenker_user
return credentials

Check warning on line 180 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L179-L180

Added lines #L179 - L180 were not covered by tests

@api.model
def _schenker_log_request(self, schenker_request, picking):
"""Helper to write raw request/response to the current picking. If debug
is active in the carrier, those will be logged in the ir.logging as well"""
schenker_last_request = schenker_last_response = False
try:
schenker_last_request = etree.tostring(

Check warning on line 188 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L186-L188

Added lines #L186 - L188 were not covered by tests
schenker_request.history.last_sent["envelope"],
encoding="UTF-8",
pretty_print=True,
)
schenker_last_response = etree.tostring(

Check warning on line 193 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L193

Added line #L193 was not covered by tests
schenker_request.history.last_received["envelope"],
encoding="UTF-8",
pretty_print=True,
)
# Don't fail hard on this. Sometimes zeep could not be able to keep history
except Exception:
return

Check warning on line 200 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L199-L200

Added lines #L199 - L200 were not covered by tests
# Debug must be active in the carrier
self.log_xml(schenker_last_request, "schenker_request")
self.log_xml(schenker_last_response, "schenker_response")

Check warning on line 203 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L202-L203

Added lines #L202 - L203 were not covered by tests

def _prepare_schenker_barcode(self):
"""Always request the barcode label when generating the booking. We can choose
between two formats: A6 and A4, where an starting position can be set"""
vals = {"barcodeRequest": self.schenker_barcode_format}

Check warning on line 208 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L208

Added line #L208 was not covered by tests
if self.schenker_barcode_mail:
vals["barcodeRequestEmail"] = self.schenker_barcode_mail

Check warning on line 210 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L210

Added line #L210 was not covered by tests
if self.schenker_barcode_format == "A6":
return vals

Check warning on line 212 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L212

Added line #L212 was not covered by tests
# This options only can be informed when the label format is A4
vals.update(

Check warning on line 214 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L214

Added line #L214 was not covered by tests
{
"start_pos": self.schenker_barcode_a4_start_pos,
"separated": self.schenker_barcode_a4_separated,
}
)
return vals

Check warning on line 220 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L220

Added line #L220 was not covered by tests

def _prepare_schenker_address(
self,
Expand All @@ -233,7 +233,7 @@
:param res.partner record
:returns dicts with shipping address formated for Scheneket API
"""
vals = {

Check warning on line 236 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L236

Added line #L236 was not covered by tests
"type": address_type,
"name1": partner.name,
"locationType": location_type, # POSTAL or PHYSICAL
Expand All @@ -248,14 +248,14 @@
}
# Optional stuff. The API doesn't like falsy or empty request fields
if partner.email:
vals["email"] = partner.email

Check warning on line 251 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L251

Added line #L251 was not covered by tests
if partner.mobile:
vals["mobilePhone"] = partner.mobile

Check warning on line 253 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L253

Added line #L253 was not covered by tests
if partner.phone:
vals["phone"] = partner.phone

Check warning on line 255 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L255

Added line #L255 was not covered by tests
if partner.street2:
vals["street2"] = partner.street2
return vals

Check warning on line 258 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L257-L258

Added lines #L257 - L258 were not covered by tests

def _schenker_shipping_address(self, picking):
"""Each booking should have at least 2 addresses of types: SHIPPER and CONSIGNEE
Expand All @@ -264,12 +264,12 @@
:param picking record
:returns list of dicts with shipping addresses formated for Scheneket API
"""
shipper_address = (

Check warning on line 267 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L267

Added line #L267 was not covered by tests
picking.picking_type_id.warehouse_id.partner_id
or picking.company_id.partner_id
)
consignee_address = picking.partner_id
return [

Check warning on line 272 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L271-L272

Added lines #L271 - L272 were not covered by tests
self._prepare_schenker_address(shipper_address, "SHIPPER"),
self._prepare_schenker_address(consignee_address),
]
Expand All @@ -278,21 +278,21 @@
"""Gets the proper shipping product according to the shipping type
:returns string with shipping product code
"""
type_mapping = {

Check warning on line 281 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L281

Added line #L281 was not covered by tests
"air": self.schenker_service_air,
"land": self.schenker_service_land,
"ocean_fcl": "fcl",
"ocean_lcl": "lcl",
}
return type_mapping[self.schenker_booking_type]

Check warning on line 287 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L287

Added line #L287 was not covered by tests

def _schenker_metric_system(self):
"""
:returns string with schenker metric system (METRIC or IMPERIAL)
"""
get_param = self.env["ir.config_parameter"].sudo().get_param
product_weight_in_lbs_param = get_param("product.weight_in_lbs", "0")
return "IMPERIAL" if product_weight_in_lbs_param == "1" else "METRIC"

Check warning on line 295 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L293-L295

Added lines #L293 - L295 were not covered by tests

def _schenker_pickup_dates(self, picking):
"""Convert picking dates for schenker api. We're taking the whole delivery
Expand All @@ -300,22 +300,22 @@
:param picking record with picking to send
:returns dict values with the picking dates in iso format
"""
date_from = fields.Datetime.context_timestamp(

Check warning on line 303 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L303

Added line #L303 was not covered by tests
self, picking.date_done.replace(hour=0, minute=0, second=0)
).isoformat()
date_to = fields.Datetime.context_timestamp(

Check warning on line 306 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L306

Added line #L306 was not covered by tests
self, picking.date_done.replace(hour=23, minute=59, second=59)
).isoformat()
return {"pickUpDateFrom": date_from, "pickUpDateTo": date_to}

Check warning on line 309 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L309

Added line #L309 was not covered by tests

def _schenker_shipping_information_package(self, picking, package):
weight = package.shipping_weight or package.weight

Check warning on line 312 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L312

Added line #L312 was not covered by tests
# Volume calculations can be unfolded with stock_quant_package_dimension
if hasattr(package, "volume"):
volume = round(package.volume, 2)

Check warning on line 315 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L315

Added line #L315 was not covered by tests
else:
volume = sum([q.quantity * q.product_id.volume for q in package.quant_ids])
return {

Check warning on line 318 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L318

Added line #L318 was not covered by tests
# Dangerous goods is not supported
"dgr": False,
"cargoDesc": picking.name + " / " + package.name,
Expand All @@ -323,12 +323,12 @@
# Default to 1 if no volume informed
"volume": volume or 0.01,
"packageType": (
package.packaging_id.shipper_package_code
or self.schenker_default_packaging_id.shipper_package_code
package.package_type_id.shipper_package_code
or self.schenker_default_package_type_id.shipper_package_code
),
"stackable": (
package.packaging_id.schenker_stackable
or self.schenker_default_packaging_id.schenker_stackable
package.package_type_id.schenker_stackable
or self.schenker_default_package_type_id.schenker_stackable
),
"pieces": 1,
}
Expand All @@ -344,7 +344,7 @@
self._schenker_shipping_information_package(picking, package)
for package in picking.package_ids
]
weight = picking.shipping_weight or picking.weight

Check warning on line 347 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L347

Added line #L347 was not covered by tests
# Obviously products should be well configured. This parameter is mandatory.
volume = sum(
[
Expand All @@ -353,7 +353,7 @@
for ml in picking.move_line_ids
]
)
return [

Check warning on line 356 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L356

Added line #L356 was not covered by tests
{
# Dangerous goods is not supported
"dgr": False,
Expand All @@ -361,8 +361,8 @@
# For a more complex solution use packaging properly
"grossWeight": round(weight / picking.number_of_packages, 2),
"volume": round(volume, 2) or 0.01,
"packageType": self.schenker_default_packaging_id.shipper_package_code,
"stackable": self.schenker_default_packaging_id.schenker_stackable,
"packageType": self.schenker_default_package_type_id.shipper_package_code,
"stackable": self.schenker_default_package_type_id.schenker_stackable,
"pieces": picking.number_of_packages,
}
]
Expand All @@ -375,22 +375,22 @@
:returns dict values for the proper unit key and value
"""
if self.schenker_measure_unit == "VOLUME":
return {"measureUnitVolume": vals["shippingInformation"]["volume"]}
return {}

Check warning on line 379 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L378-L379

Added lines #L378 - L379 were not covered by tests

def _prepare_schenker_shipping(self, picking):
"""Convert picking values for schenker api
:param picking record with picking to send
:returns dict values for the connector
"""
self.ensure_one()

Check warning on line 386 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L386

Added line #L386 was not covered by tests
# We'll compose the request via some diferenced parts, like label settings,
# address options, incoterms and so. There are lots of thing to take into
# account to acomplish a properly formed request.
vals = {}
vals.update(self._prepare_schenker_barcode())
shipping_information = self._schenker_shipping_information(picking)
vals.update(

Check warning on line 393 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L390-L393

Added lines #L390 - L393 were not covered by tests
{
"address": self._schenker_shipping_address(picking),
"incoterm": (
Expand All @@ -404,7 +404,7 @@
"shippingInformation": {
"shipmentPosition": shipping_information,
"grossWeight": round(picking.shipping_weight, 2),
"volume": shipping_information["volume"],
"volume": shipping_information[0]["volume"],
},
"measureUnit": self.schenker_measure_unit,
# Customs Clearance not supported for now as it needs a full customs
Expand All @@ -427,8 +427,8 @@
"pharmaceuticals": self.schenker_pharmaceuticals,
}
)
vals.update(self._schenker_measures(picking, vals))
return vals

Check warning on line 431 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L430-L431

Added lines #L430 - L431 were not covered by tests

def schenker_send_shipping(self, pickings):
"""Send booking request to Schenker
Expand All @@ -438,83 +438,83 @@
to this design, we have to inject vals in the context to be able to
add them to the message.
"""
schenker_request = SchenkerRequest(**self._get_schenker_credentials())
result = []

Check warning on line 442 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L441-L442

Added lines #L441 - L442 were not covered by tests
for picking in pickings:
vals = self._prepare_schenker_shipping(picking)
vals.update({"tracking_number": False, "exact_price": 0})
try:
response = schenker_request._send_shipping(

Check warning on line 447 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L444-L447

Added lines #L444 - L447 were not covered by tests
vals, self.schenker_booking_type
)
except Exception as e:
raise (e)

Check warning on line 451 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L450-L451

Added lines #L450 - L451 were not covered by tests
finally:
self._schenker_log_request(schenker_request, picking)
if not response:
result.append(vals)
continue
vals["tracking_number"] = response.get("booking_id")

Check warning on line 457 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L455-L457

Added lines #L455 - L457 were not covered by tests
# We post an extra message in the chatter with the barcode and the
# label because there's clean way to override the one sent by core.
body = _("Schenker Shipping barcode document")
attachment = []

Check warning on line 461 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L460-L461

Added lines #L460 - L461 were not covered by tests
if response.get("barcode"):
attachment = [

Check warning on line 463 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L463

Added line #L463 was not covered by tests
(
"schenker_label_{}.pdf".format(response.get("booking_id")),
response.get("barcode"),
)
]
picking.message_post(body=body, attachments=attachment)
result.append(vals)
return result

Check warning on line 471 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L469-L471

Added lines #L469 - L471 were not covered by tests

def schenker_cancel_shipment(self, pickings):
"""Cancel the expedition
:param pickings - stock.picking recordset
:returns pdf file
"""
schenker_request = SchenkerRequest(**self._get_schenker_credentials())

Check warning on line 478 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L478

Added line #L478 was not covered by tests
for picking in pickings.filtered("carrier_tracking_ref"):
try:
schenker_request._cancel_shipment(picking.carrier_tracking_ref)
except Exception as e:
raise (e)

Check warning on line 483 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L480-L483

Added lines #L480 - L483 were not covered by tests
finally:
self._schenker_log_request(schenker_request, picking)
return True

Check warning on line 486 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L486

Added line #L486 was not covered by tests

def schenker_get_label(self, reference):
"""Generate label for picking
:param picking - stock.picking record
:returns pdf file
"""
self.ensure_one()

Check warning on line 493 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L493

Added line #L493 was not covered by tests
if not reference:
return False
schenker_request = SchenkerRequest(**self._get_schenker_credentials())
format_vals = self.schenker_barcode_format

Check warning on line 497 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L495-L497

Added lines #L495 - L497 were not covered by tests
if format_vals == "A4":
format_vals = {

Check warning on line 499 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L499

Added line #L499 was not covered by tests
"start_pos": self.schenker_barcode_a4_start_pos,
"_value_1": self.schenker_barcode_format,
}
label = schenker_request._shipping_label([reference], format_vals)

Check warning on line 503 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L503

Added line #L503 was not covered by tests
if not label:
return False
return label

Check warning on line 506 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L505-L506

Added lines #L505 - L506 were not covered by tests

def schenker_get_tracking_link(self, picking):
"""Provide tracking link for the customer"""
return (

Check warning on line 510 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L510

Added line #L510 was not covered by tests
"https://eschenker.dbschenker.com/app/tracking-public/?refNumber=%s"
% picking.carrier_tracking_ref
)

def _prepare_schenker_tracking(self, picking):
self.ensure_one()
return {

Check warning on line 517 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L516-L517

Added lines #L516 - L517 were not covered by tests
"reference": picking.carrier_tracking_ref,
"reference_type": "cu",
"booking_type": self.schenker_booking_type,
Expand All @@ -522,20 +522,20 @@

def schenker_tracking_state_update(self, picking):
"""Tracking state update"""
self.ensure_one()

Check warning on line 525 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L525

Added line #L525 was not covered by tests
if not picking.carrier_tracking_ref:
return
schenker_request = SchenkerRequest(

Check warning on line 528 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L527-L528

Added lines #L527 - L528 were not covered by tests
**self._get_schenker_credentials(), service="tracking"
)
response = schenker_request._get_tracking_states(

Check warning on line 531 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L531

Added line #L531 was not covered by tests
**self._prepare_schenker_tracking(picking)
)
if response.get("shipment"):
shipment = response.get("shipment")[0]
info = shipment.ShipmentInfo.ShipmentBasicInfo
status_event_list = info.StatusEventList.StatusEvent
last_event = SCHENKER_STATUS_CODES.get(info.LastEvent, ("",))

Check warning on line 538 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L535-L538

Added lines #L535 - L538 were not covered by tests
picking.write(
{
"tracking_state_history": (
Expand All @@ -558,11 +558,11 @@
"delivery_state": last_event[1],
}
)
return

Check warning on line 561 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L561

Added line #L561 was not covered by tests

def schenker_rate_shipment(self, order):
"""There's no public API so another price method should be used."""
return {

Check warning on line 565 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L565

Added line #L565 was not covered by tests
"success": True,
"price": self.product_id.lst_price,
"error_message": _(
Expand All @@ -585,7 +585,7 @@
the future, this can be removed as long as those method have the proper
support"""
if self.schenker_booking_type != "land":
raise UserError(_("Only land shipping is currently supported"))

Check warning on line 588 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L588

Added line #L588 was not covered by tests

@api.onchange("schenker_measure_unit")
def onchange_schenker_measure_unit(self):
Expand All @@ -593,4 +593,4 @@
calculation structure should be provided to use the other API options. A hook
method is provided though."""
if self.schenker_measure_unit != "VOLUME":
raise UserError(_("Only volume is currently supported"))

Check warning on line 596 in delivery_schenker/models/delivery_carrier.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/delivery_carrier.py#L596

Added line #L596 was not covered by tests
9 changes: 7 additions & 2 deletions delivery_schenker/models/schenker_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@
def __init__(
self, access_key=None, group_id=None, user=None, prod=False, service="booking"
):
self.access_key = access_key or ""
self.group_id = group_id or ""
self.user = user or ""
self.service = service
api_env = "prod" if prod else "test"
self.history = HistoryPlugin(maxlen=10)
settings = Settings(strict=False, xml_huge_tree=True)
self.client = Client(

Check warning on line 44 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L37-L44

Added lines #L37 - L44 were not covered by tests
wsdl=SCHENKER_API_URL[api_env] + SCHENKER_API_SERVICE[service],
settings=settings,
plugins=[self.history],
Expand All @@ -51,37 +51,42 @@
"""Schenker API returns error petitions as server exceptions wich makes zeep to
raise a Fault exception as well. To catch the error info we need to make a
raw_response request and the extract the error codes from the response."""
try:

Check warning on line 54 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L54

Added line #L54 was not covered by tests
if not send_as_kw:
response = service(vals)

Check warning on line 56 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L56

Added line #L56 was not covered by tests
else:
response = service(**vals)
except Fault as e:
with self.client.settings(raw_response=True):

Check warning on line 60 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L58-L60

Added lines #L58 - L60 were not covered by tests
if not send_as_kw:
response = service(vals)

Check warning on line 62 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L62

Added line #L62 was not covered by tests
else:
response = service(**vals)
try:
root = ET.fromstring(response.text)
error_text = next(root.iter("faultstring")).text
error_message = next(root.iter("message")).text
error_code = next(root.iter("code")).text
raise ValidationError(

Check warning on line 70 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L64-L70

Added lines #L64 - L70 were not covered by tests
_(
"Error in the request to the Schenker API. This is the "
"thrown message:\n\n"
"[%s]\n"
"%s - %s" % (error_text, error_code, error_message)
"[%(error_text)s]\n"
"%(error_code)s - %(error_message)s"
)
% {
"error_text": error_text,
"error_code": error_code,
"error_message": error_message,
}
) from e
except ValidationError:
raise

Check warning on line 84 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L84

Added line #L84 was not covered by tests
# If we can't get the proper exception, fallback to the first
# exception error traceback
except Exception as exc:
raise Fault(e) from exc
return response

Check warning on line 89 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L87-L89

Added lines #L87 - L89 were not covered by tests

# Booking API methods

Expand All @@ -91,101 +96,101 @@
:params string with shipping method
:returns string with the mapped key value for the proper method
"""
method_map = {

Check warning on line 99 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L99

Added line #L99 was not covered by tests
"land": "getBookingRequestLand",
"air": "getBookingRequestAir",
"ocean_fcl": "getBookingRequestOceanFCL",
"ocean_lcl": "getBookingRequestOceanLCL",
}
return method_map.get("method", "getBookingRequestLand")

Check warning on line 105 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L105

Added line #L105 was not covered by tests

def _shipping_api_credentials(self):
"""Each API has a different credentials SOAP declaration"""
credentials = {"applicationArea": {"accessKey": self.access_key}}

Check warning on line 109 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L109

Added line #L109 was not covered by tests
if self.user:
credentials["applicationArea"]["userId"] = self.user

Check warning on line 111 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L111

Added line #L111 was not covered by tests
if self.group_id:
credentials["applicationArea"]["groupId"] = self.group_id
return credentials

Check warning on line 114 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L113-L114

Added lines #L113 - L114 were not covered by tests

def _scheneker_shipping_api_wrapper(self, method=False):
"""Aside from a different API method, each one has its own wrapper"""
booking_wrapper_map = {

Check warning on line 118 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L118

Added line #L118 was not covered by tests
"land": "bookingLand",
"air": "bookingAir",
"ocean_fcl": "bookingOceanFCL",
"ocean_lcl": "bookingOceanLCL",
}
return booking_wrapper_map.get(method, "land")

Check warning on line 124 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L124

Added line #L124 was not covered by tests

def _send_shipping(self, picking_vals, method=False):
"""Create new shipment
:params vals dict of needed values
:returns dict with Schenker response containing the shipping code and label
"""
vals = self._shipping_api_credentials()
method_wrapper = self._scheneker_shipping_api_wrapper(method)
vals[method_wrapper] = picking_vals

Check warning on line 133 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L131-L133

Added lines #L131 - L133 were not covered by tests
# From the Schenker docs:
# Defines if booking shall be submitted. If false, the booking can be edited
# in the frontend and MUST be submitted manually.
vals[method_wrapper].update({"submitBooking": True})
response = self._process_reply(

Check warning on line 138 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L137-L138

Added lines #L137 - L138 were not covered by tests
self.client.service[self._shipping_type_method(method)], vals
)
return {"booking_id": response.bookingId, "barcode": response.barcodeDocument}

Check warning on line 141 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L141

Added line #L141 was not covered by tests

def _shipping_label(self, reference_list=None, label_format="A6"):
"""Get shipping label for the given ref
:param list reference -- shipping reference list
:returns: base64 with pdf labels
"""
reference_list = reference_list or []
vals = dict(

Check warning on line 149 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L148-L149

Added lines #L148 - L149 were not covered by tests
**self._shipping_api_credentials(),
**{"barcodeRequest": {"format": label_format}}
)
vals["barcodeRequest"].update({"bookingId": ref for ref in reference_list})
label = self._process_reply(

Check warning on line 154 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L154

Added line #L154 was not covered by tests
self.client.service.getBookingBarcodeRequest, vals
).document
return label

Check warning on line 157 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L157

Added line #L157 was not covered by tests

def _cancel_shipment(self, reference=False):
"""Cancel de expedition for the given ref
:param str reference -- booking reference string
:returns: bool True if success
"""
vals = self._shipping_api_credentials()
vals.update({"cancelRequest": {"bookingId": reference}})
response = self._process_reply(

Check warning on line 166 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L164-L166

Added lines #L164 - L166 were not covered by tests
self.client.service.getBookingCancelRequest, vals
)
# TODO: Inspect typical response as we don't want to return a zeep object.
# Anyway, it's going to fail if the booking can't be cancelled. So either we
# receive an exception error or the booking is cancelled.
return bool(response)

Check warning on line 172 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L172

Added line #L172 was not covered by tests

# Tracking API methods

def _tracking_api_credentials(self):
"""Each API has a different credentials SOAP declaration"""
return {"AccessKey": self.access_key, "in": {}}

Check warning on line 178 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L178

Added line #L178 was not covered by tests

def _get_tracking_states(
self, reference=False, reference_type="cu", booking_type="land"
):
if not reference:
return {}
vals = self._tracking_api_credentials()
vals["in"].update(

Check warning on line 186 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L184-L186

Added lines #L184 - L186 were not covered by tests
{
"referenceNumber": reference,
"referenceType": reference_type,
"transportNature": "exp" if booking_type == "land" else "int",
}
)
response = self._process_reply(

Check warning on line 193 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L193

Added line #L193 was not covered by tests
self.client.service.getPublicShipmentDetails, vals, send_as_kw=True
)
return {"shipment": response.Shipment}

Check warning on line 196 in delivery_schenker/models/schenker_request.py

View check run for this annotation

Codecov / codecov/patch

delivery_schenker/models/schenker_request.py#L196

Added line #L196 was not covered by tests
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# Copyright 2021 Tecnativa - David Vidal
# Copyright 2023 Studio73 - Ferran Mora
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models


class ProductPackaging(models.Model):
_inherit = "product.packaging"
class StockPackageType(models.Model):
_inherit = "stock.package.type"

package_carrier_type = fields.Selection(selection_add=[("schenker", "DB Schenker")])
schenker_stackable = fields.Boolean(
Expand Down
2 changes: 1 addition & 1 deletion delivery_schenker/views/delivery_schenker_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
</group>
<group string="Logistics">
<field
name="schenker_default_packaging_id"
name="schenker_default_package_type_id"
attrs="{'required': [('delivery_type', '=', 'schenker')]}"
/>
<field
Expand Down
2 changes: 1 addition & 1 deletion delivery_schenker/views/stock_picking_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
<record id="view_picking_withcarrier_out_form" model="ir.ui.view">
<field name="model">stock.picking</field>
<field name="inherit_id" ref="delivery.view_picking_withcarrier_out_form" />
<field name="groups_id" eval="[(4, ref('base.group_no_one'))]" />
<field name="arch" type="xml">
<xpath expr="//header" position='inside'>
<button
name="schenker_get_label"
string="Schenker Label"
type="object"
groups="base.group_no_one"
attrs="{
'invisible':[
'|',
Expand Down
Loading