first commit
This commit is contained in:
285
modules/stock_package_shipping_mygls/stock.py
Normal file
285
modules/stock_package_shipping_mygls/stock.py
Normal file
@@ -0,0 +1,285 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
from io import BytesIO
|
||||
from itertools import zip_longest
|
||||
|
||||
from pypdf import PdfReader, PdfWriter
|
||||
|
||||
from trytond.i18n import gettext
|
||||
from trytond.model import fields
|
||||
from trytond.model.exceptions import AccessError
|
||||
from trytond.modules.stock_package_shipping.exceptions import (
|
||||
PackingValidationError)
|
||||
from trytond.modules.stock_package_shipping.stock import address_name
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.wizard import StateAction, StateTransition, Wizard
|
||||
|
||||
from .api import get_client, get_request
|
||||
from .exceptions import MyGLSError
|
||||
|
||||
|
||||
def iter_pdf_pages(document):
|
||||
if hasattr(document, 'pages'):
|
||||
yield from document.pages
|
||||
else:
|
||||
for i in range(document.getNumPages()):
|
||||
yield document.getPage(i)
|
||||
|
||||
|
||||
class Package(metaclass=PoolMeta):
|
||||
__name__ = 'stock.package'
|
||||
|
||||
mygls_shipping_id = fields.Integer("ID", readonly=True)
|
||||
|
||||
@classmethod
|
||||
def copy(cls, packages, default=None):
|
||||
default = {} if default is None else default.copy()
|
||||
default.setdefault('mygls_shipping_id')
|
||||
return super().copy(packages, default=default)
|
||||
|
||||
|
||||
class ShippingMyGLSMixin:
|
||||
__slots__ = ()
|
||||
|
||||
def get_mygls_credential(self):
|
||||
pool = Pool()
|
||||
Credential = pool.get('carrier.credential.mygls')
|
||||
|
||||
pattern = self._get_mygls_credential_pattern()
|
||||
for credential in Credential.search([]):
|
||||
if credential.match(pattern):
|
||||
return credential
|
||||
|
||||
def _get_mygls_credential_pattern(self):
|
||||
return {
|
||||
'company': self.company.id,
|
||||
'country': self.shipping_warehouse.address.country.code.lower(),
|
||||
}
|
||||
|
||||
def validate_packing_mygls(self, usage=None):
|
||||
warehouse = self.shipping_warehouse
|
||||
if not warehouse.address:
|
||||
raise PackingValidationError(
|
||||
gettext('stock_package_shipping_mygls'
|
||||
'.msg_warehouse_address_required',
|
||||
shipment=self.rec_name,
|
||||
warehouse=warehouse.rec_name))
|
||||
if not warehouse.address.country or not warehouse.address.country.code:
|
||||
raise PackingValidationError(
|
||||
gettext('stock_package_shipping_mygls'
|
||||
'.msg_warehouse_address_country_code_required',
|
||||
shipment=self.rec_name,
|
||||
warehouse=warehouse.rec_name))
|
||||
shipping_to_address = self.shipping_to_address
|
||||
if (not shipping_to_address.country
|
||||
or not shipping_to_address.country.code):
|
||||
raise PackingValidationError(
|
||||
gettext('stock_package_shipping_mygls'
|
||||
'.msg_shipping_to_address_country_code_required',
|
||||
shipping=self.rec_name))
|
||||
if (self.carrier.mygls_services
|
||||
and 'CS1' in self.carrier.mygls_services):
|
||||
if not shipping_to_address.contact_mechanism_get(
|
||||
{'phone', 'mobile'}, usage=usage):
|
||||
raise PackingValidationError(
|
||||
gettext('stock_package_shipping_mygls'
|
||||
'.msg_phone_mobile_required',
|
||||
shipment=self.rec_name,
|
||||
address=shipping_to_address.rec_name))
|
||||
if (self.carrier.mygls_services
|
||||
and 'FDS' in self.carrier.mygls_services):
|
||||
if not shipping_to_address.contact_mechanism_get(
|
||||
'email', usage=usage):
|
||||
raise PackingValidationError(
|
||||
gettext('stock_package_shipping_mygls'
|
||||
'.msg_email_required',
|
||||
shipment=self.rec_name,
|
||||
address=shipping_to_address.rec_name))
|
||||
if (self.carrier.mygls_services
|
||||
and ('FSS' in self.carrier.mygls_services
|
||||
or 'SM1' in self.carrier.mygls_services
|
||||
or 'SM2' in self.carrier.mygls_services)):
|
||||
if not shipping_to_address.contact_mechanism_get(
|
||||
'mobile', usage=usage):
|
||||
raise PackingValidationError(
|
||||
gettext('stock_package_shipping_mygls'
|
||||
'.msg_mobile_required',
|
||||
shipment=self.rec_name,
|
||||
address=shipping_to_address.rec_name))
|
||||
|
||||
|
||||
class ShipmentCreateShipping(metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.create_shipping'
|
||||
|
||||
mygls = StateAction(
|
||||
'stock_package_shipping_mygls.act_create_shipping_mygls_wizard')
|
||||
|
||||
def transition_start(self):
|
||||
next_state = super().transition_start()
|
||||
if self.record.carrier.shipping_service == 'mygls':
|
||||
next_state = 'mygls'
|
||||
return next_state
|
||||
|
||||
def do_mygls(self, action):
|
||||
ctx = Transaction().context
|
||||
return action, {
|
||||
'model': ctx['active_model'],
|
||||
'id': ctx['active_id'],
|
||||
'ids': [ctx['active_id']],
|
||||
}
|
||||
|
||||
|
||||
class ShipmentCreateShippingMyGLS(Wizard):
|
||||
__name__ = 'stock.shipment.create_shipping.mygls'
|
||||
|
||||
start = StateTransition()
|
||||
|
||||
def transition_start(self):
|
||||
pool = Pool()
|
||||
Package = pool.get('stock.package')
|
||||
|
||||
shipment = self.record
|
||||
if shipment.shipping_reference:
|
||||
raise AccessError(
|
||||
gettext('stock_package_shipping_mygls'
|
||||
'.msg_shipment_has_shipping_reference_number',
|
||||
shipment=shipment.rec_name))
|
||||
|
||||
credential = shipment.get_mygls_credential()
|
||||
client = get_client(credential, 'ParcelService')
|
||||
carrier = shipment.carrier
|
||||
packages = shipment.root_packages
|
||||
|
||||
parcel = self.get_parcel(shipment, packages, credential)
|
||||
|
||||
response = client.service.PrintLabels(**get_request(
|
||||
credential, 'printLabelsRequest',
|
||||
ParcelList=[{'Parcel': parcel}],
|
||||
TypeOfPrinter=carrier.mygls_type_of_printer,
|
||||
PrintPosition=carrier.mygls_print_position))
|
||||
|
||||
if response.PrintLabelsErrorList:
|
||||
message = "\n".join(
|
||||
e.ErrorDescription
|
||||
for e in response.PrintLabelsErrorList.ErrorInfo)
|
||||
raise MyGLSError(
|
||||
gettext('stock_package_shipping_mygls.msg_mygls_api_error',
|
||||
message=message))
|
||||
|
||||
labels = []
|
||||
reader = PdfReader(BytesIO(response.Labels))
|
||||
for i, page in enumerate(iter_pdf_pages(reader)):
|
||||
pdf = PdfWriter()
|
||||
label = BytesIO()
|
||||
pdf.add_page(page)
|
||||
pdf.write(label)
|
||||
if carrier.mygls_type_of_printer in {'A4_2x2', 'A4_4x1'}:
|
||||
if carrier.mygls_type_of_printer == 'A4_2x2':
|
||||
gap = carrier.mygls_print_position - 1
|
||||
else:
|
||||
gap = 0
|
||||
max_label = ((i + 1) * 4) - gap
|
||||
n = 4
|
||||
if i == 0:
|
||||
n -= gap
|
||||
if max_label > len(packages):
|
||||
n -= max_label - len(packages)
|
||||
labels.extend([label] * n)
|
||||
else:
|
||||
labels.append(label)
|
||||
labels_info = response.PrintLabelsInfoList.PrintLabelsInfo
|
||||
|
||||
for package, label, info in zip_longest(
|
||||
packages, labels, labels_info):
|
||||
package.mygls_shipping_id = info.ParcelId
|
||||
package.shipping_label = fields.Binary.cast(label.getvalue())
|
||||
package.shipping_label_mimetype = carrier.shipping_label_mimetype
|
||||
package.shipping_reference = info.ParcelNumber
|
||||
if not shipment.shipping_reference:
|
||||
shipment.shipping_reference = info.ParcelNumber
|
||||
Package.save(packages)
|
||||
shipment.save()
|
||||
|
||||
return 'end'
|
||||
|
||||
def get_parcel(self, shipment, packages, credential):
|
||||
services = []
|
||||
if shipment.carrier.mygls_services:
|
||||
for code in shipment.carrier.mygls_services:
|
||||
services.append({'Service': self.get_service(code, shipment)})
|
||||
return {
|
||||
'ClientNumber': credential.client_number,
|
||||
'ClientReference': shipment.number,
|
||||
'Count': len(packages),
|
||||
'Content': shipment.shipping_description,
|
||||
'PickupAddress': self.get_address(
|
||||
shipment.company.party,
|
||||
shipment.shipping_warehouse.address),
|
||||
'DeliveryAddress': self.get_address(
|
||||
shipment.shipping_to,
|
||||
shipment.shipping_to_address),
|
||||
'ServiceList': services,
|
||||
}
|
||||
|
||||
def get_address(self, party, address, usage=None):
|
||||
if address.street_unstructured:
|
||||
street_name = address.street_single_line
|
||||
house_number = ''
|
||||
house_number_info = ''
|
||||
else:
|
||||
street_name = address.street_name or ''
|
||||
house_number = address.numbers or ''
|
||||
house_number_info = address.building_name or ''
|
||||
phone = address.contact_mechanism_get({'phone', 'mobile'}, usage=usage)
|
||||
email = address.contact_mechanism_get('email', usage=usage)
|
||||
name = address_name(address, party)
|
||||
contact_name = party.full_name if party.full_name != name else None
|
||||
return {
|
||||
'Name': name,
|
||||
'Street': street_name,
|
||||
'HouseNumber': house_number,
|
||||
'HouseNumberInfo': house_number_info,
|
||||
'City': address.city,
|
||||
'ZipCode': address.postal_code,
|
||||
'CountryIsoCode': address.country.code,
|
||||
'ContactName': contact_name,
|
||||
'ContactPhone': phone.value if phone else None,
|
||||
'ContactEmail': email.value if email else None,
|
||||
}
|
||||
|
||||
def get_service(self, code, shipment, usage=None):
|
||||
pool = Pool()
|
||||
Carrier = pool.get('carrier')
|
||||
service = {'Code': code}
|
||||
shipping_to_address = shipment.shipping_to_address
|
||||
if code == 'AOS':
|
||||
service['AOSParameter'] = shipment.shipping_to.full_name,
|
||||
elif code == 'CS1':
|
||||
phone = shipping_to_address.contact_mechanism_get(
|
||||
{'phone', 'mobile'}, usage=usage)
|
||||
service['CS1Parameter'] = phone.value
|
||||
elif code == 'FDS':
|
||||
email = shipping_to_address.contact_mechanism_get(
|
||||
'email', usage=usage)
|
||||
service['FDSParameter'] = email.value
|
||||
elif code == 'FSS':
|
||||
mobile = shipping_to_address.contact_mechanism_get(
|
||||
'mobile', usage=usage)
|
||||
service['FSSParameter'] = mobile.value
|
||||
elif code == 'SM1':
|
||||
if shipment.shipping_to.lang:
|
||||
lang_code = shipment.shipping_to.lang.code
|
||||
else:
|
||||
lang_code = None
|
||||
with Transaction().set_context(language=lang_code):
|
||||
carrier = Carrier(shipment.carrier.id)
|
||||
mobile = shipping_to_address.contact_mechanism_get(
|
||||
'mobile', usage=usage)
|
||||
service['SM1Parameter'] = '|'.join([
|
||||
mobile.value, carrier.mygls_sms])
|
||||
elif code == 'SM2':
|
||||
mobile = shipping_to_address.contact_mechanism_get(
|
||||
'mobile', usage=usage)
|
||||
service['SM1Parameter'] = mobile.value
|
||||
return service
|
||||
Reference in New Issue
Block a user