Skip to content

Commit 7b62d91

Browse files
author
Michael Michot
committed
[WIP][ADD] Module phs delivery label
1 parent 85edf77 commit 7b62d91

File tree

10 files changed

+452
-0
lines changed

10 files changed

+452
-0
lines changed

phs_delivery_label/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from . import models
2+
3+
# from . import tests

phs_delivery_label/__manifest__.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Copyright 2020 Pharmasimple (https://www.pharmasimple.be)
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
{
4+
"name": "PHS Delivery label",
5+
"category": "Delivery",
6+
"summary": "Pharmasimple delivery carrier label",
7+
"version": "14.0.1.0.0",
8+
"author": "Pharmasimple",
9+
"license": "AGPL-3",
10+
"website": "https://github.com/akretion/phs-addons",
11+
"depends": [
12+
"base",
13+
"delivery",
14+
],
15+
"data": [
16+
"views/report_label.xml",
17+
"report/phs_label_reports.xml",
18+
],
19+
"installable": True,
20+
"application": False,
21+
}

phs_delivery_label/models/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from . import delivery_carrier
2+
from . import stock_picking
3+
from . import stock_quant_package
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from odoo import fields, models
2+
3+
4+
class DeliveryCarrier(models.Model):
5+
""" Add service group """
6+
7+
_inherit = "delivery.carrier"
8+
9+
delivery_type = fields.Selection(
10+
selection_add=[("pharmasimple", "Pharmasimple")],
11+
ondelete={"pharmasimple": "set default"},
12+
)
13+
14+
pharmasimple_default_packaging_id = fields.Many2one(
15+
"product.packaging", domain=[("package_carrier_type", "=", "pharmasimple")]
16+
)
17+
18+
def pharmasimple_rate_shipment(self, order):
19+
self.ensure_one()
20+
delivery_product_price = self.product_id and self.product_id.lst_price or 0
21+
return {
22+
"success": True,
23+
"price": delivery_product_price,
24+
"error_message": False,
25+
"warning_message": False,
26+
}
27+
28+
def pharmasimple_send_shipping(self, pickings):
29+
"""
30+
It will generate the labels for all the packages of the picking.
31+
Packages are mandatory in this case
32+
"""
33+
for pick in pickings:
34+
pick._set_a_default_package()
35+
pick._generate_pharmasimple_label()
36+
37+
return [{"exact_price": False, "tracking_number": False}]
+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import base64
2+
import re
3+
from operator import attrgetter
4+
5+
from odoo import exceptions, models
6+
7+
_compile_itemid = re.compile(r"[^0-9A-Za-z+\-_]")
8+
9+
10+
class StockPicking(models.Model):
11+
_inherit = "stock.picking"
12+
13+
def get_shipping_label_values(self, label):
14+
self.ensure_one()
15+
return {
16+
"name": label["name"],
17+
"res_id": self.id,
18+
"res_model": "stock.picking",
19+
"datas": label["file"],
20+
"file_type": label["file_type"],
21+
}
22+
23+
def _set_a_default_package(self):
24+
"""Pickings using this module must have a package
25+
If not this method put it one silently
26+
"""
27+
for picking in self:
28+
move_lines = picking.move_line_ids.filtered(
29+
lambda s: not (s.package_id or s.result_package_id)
30+
)
31+
if move_lines:
32+
carrier = picking.carrier_id
33+
default_packaging = carrier.pharmasimple_default_packaging_id
34+
package = self.env["stock.quant.package"].create(
35+
{
36+
"packaging_id": default_packaging
37+
and default_packaging.id
38+
or False
39+
}
40+
)
41+
move_lines.write({"result_package_id": package.id})
42+
43+
def _get_packages_from_picking(self):
44+
""" Get all the packages from the picking """
45+
self.ensure_one()
46+
operation_obj = self.env["stock.move.line"]
47+
operations = operation_obj.search(
48+
[
49+
"|",
50+
("package_id", "!=", False),
51+
("result_package_id", "!=", False),
52+
("picking_id", "=", self.id),
53+
]
54+
)
55+
package_ids = set()
56+
for operation in operations:
57+
# Take the destination package. If empty, the package is
58+
# moved so take the source one.
59+
package_ids.add(operation.result_package_id.id or operation.package_id.id)
60+
61+
packages = self.env["stock.quant.package"].browse(package_ids)
62+
return packages
63+
64+
def info_from_label(self, label):
65+
data = base64.b64decode(label["binary"])
66+
67+
return {
68+
"file": data,
69+
"file_type": label["file_type"],
70+
"name": "1234" + "." + label["file_type"],
71+
}
72+
73+
def write_tracking_number_label(self, label_result, packages):
74+
"""
75+
If there are no pack defined, write tracking_number on picking
76+
otherwise, write it on parcel_tracking field of each pack.
77+
Note we can receive multiple labels for a same package
78+
"""
79+
80+
labels = []
81+
82+
# It could happen that no successful label has been returned by the API
83+
if not label_result:
84+
return labels
85+
86+
if not packages:
87+
label = label_result[0]["value"][0]
88+
self.carrier_tracking_ref = label["tracking_number"]
89+
labels.append(self.info_from_label(label))
90+
91+
tracking_refs = []
92+
for package in packages:
93+
tracking_numbers = []
94+
for label in label_result:
95+
for label_value in label["value"]:
96+
if package.name in label_value["item_id"].split("+")[-1]:
97+
tracking_numbers.append(label_value["tracking_number"])
98+
labels.append(self.info_from_label(label_value))
99+
package.parcel_tracking = "; ".join(tracking_numbers)
100+
tracking_refs += tracking_numbers
101+
102+
existing_tracking_ref = (
103+
self.carrier_tracking_ref and self.carrier_tracking_ref.split("; ") or []
104+
)
105+
self.carrier_tracking_ref = "; ".join(existing_tracking_ref + tracking_refs)
106+
return labels
107+
108+
def _get_itemid(self, picking, pack_no):
109+
"""Allowed characters are alphanumeric plus `+`, `-` and `_`
110+
Last `+` separates picking name and package number (if any)
111+
112+
:return string: itemid
113+
114+
"""
115+
name = _compile_itemid.sub("", picking.name)
116+
if not pack_no:
117+
return name
118+
119+
pack_no = _compile_itemid.sub("", pack_no)
120+
codes = [name, pack_no]
121+
return "+".join(c for c in codes if c)
122+
123+
def generate_label(self, picking, packages):
124+
results = []
125+
126+
for package in packages:
127+
file_type = "pdf"
128+
pdf, pdf_format = (
129+
self.env.ref("phs_delivery_label.phs_delivery_label_report")
130+
.sudo()
131+
._render_qweb_pdf(picking.ids)
132+
)
133+
# binary = base64.b64encode(bytes(pdf, "utf-8"))
134+
binary = pdf
135+
res = {"value": []}
136+
res["success"] = True
137+
res["value"].append(
138+
{
139+
"item_id": self._get_itemid(
140+
picking, package.name if package else None
141+
),
142+
"binary": binary,
143+
"tracking_number": "1234",
144+
"file_type": file_type,
145+
}
146+
)
147+
results.append(res)
148+
return results
149+
150+
def _generate_pharmasimple_label(self, package_ids=None):
151+
""" Generate labels """
152+
self.ensure_one()
153+
154+
if package_ids is None:
155+
packages = self._get_packages_from_picking()
156+
packages = packages.sorted(key=attrgetter("name"))
157+
else:
158+
# restrict on the provided packages
159+
package_obj = self.env["stock.quant.package"]
160+
packages = package_obj.browse(package_ids)
161+
162+
# Do not generate label for packages that are already done
163+
packages = packages.filtered(lambda p: not p.parcel_tracking)
164+
165+
label_results = self.generate_label(self, packages)
166+
167+
# Process the success packages first
168+
success_label_results = [
169+
label for label in label_results if "errors" not in label
170+
]
171+
failed_label_results = [label for label in label_results if "errors" in label]
172+
173+
# Case when there is a failed label, rollback odoo data
174+
if failed_label_results:
175+
self._cr.rollback()
176+
177+
labels = self.write_tracking_number_label(success_label_results, packages)
178+
179+
if failed_label_results:
180+
# Commit the change to save the changes,
181+
# This ensures the label pushed recored correctly in Odoo
182+
self._cr.commit() # pylint: disable=invalid-commit
183+
error_message = "\n".join(label["errors"] for label in failed_label_results)
184+
raise exceptions.Warning(error_message)
185+
186+
return labels
187+
188+
def generate_pharmasimple_shipping_labels(self, package_ids=None):
189+
""" Add label generation for Pharmasimple """
190+
self.ensure_one()
191+
return self._generate_pharmasimple_label(package_ids=package_ids)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from odoo import fields, models
2+
3+
4+
class StockQuantPackage(models.Model):
5+
_inherit = "stock.quant.package"
6+
7+
parcel_tracking = fields.Char("Parcel Tracking")
8+
package_carrier_type = fields.Selection(
9+
related="packaging_id.package_carrier_type",
10+
string="Packaging's Carrier",
11+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<openerp>
3+
<data>
4+
5+
<record id="phs_delivery_label_report" model="ir.actions.report">
6+
<field name="name">Phs delivery label</field>
7+
<field name="model">stock.picking</field>
8+
<field name="report_type">qweb-pdf</field>
9+
<field
10+
name="report_name"
11+
>phs_delivery_label.report_pharmasimple_label</field>
12+
<field
13+
name="report_file"
14+
>phs_delivery_label.report_pharmasimple_label</field>
15+
<field name="print_report_name">(object._get_report_base_filename())</field>
16+
<field name="attachment">(object.name+'.pdf')</field>
17+
<field name="binding_model_id" ref="model_stock_picking" />
18+
<field name="binding_type">report</field>
19+
</record>
20+
21+
<record id="paperformat_pharmasimple_label" model="report.paperformat">
22+
<field name="name">Pharmasimple Paper Format</field>
23+
<field name="default" eval="True" />
24+
<field name="format">custom</field>
25+
<field name="page_height">150</field>
26+
<field name="page_width">100</field>
27+
<field name="orientation">Portrait</field>
28+
<field name="margin_top">4</field>
29+
<field name="margin_bottom">1</field>
30+
<field name="margin_left">1</field>
31+
<field name="margin_right">1</field>
32+
<field name="header_line" eval="False" />
33+
<field name="header_spacing">0</field>
34+
<field name="dpi">90</field>
35+
<field
36+
name="report_ids"
37+
eval="[(6,0,[ref('phs_delivery_label.phs_delivery_label_report')])]"
38+
/>
39+
</record>
40+
41+
</data>
42+
</openerp>

0 commit comments

Comments
 (0)