Files
enterprise-addons/delivery_envia/models/delivery_carrier.py
T

291 lines
11 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
from urllib.parse import urlencode
from odoo import fields, models, api
from odoo.exceptions import ValidationError, UserError
from .envia_request import Envia
_logger = logging.getLogger(__name__)
ENVIA_STOCK_TYPE = [
('PAPER_4X6', 'PAPER_4X6'),
('PAPER_4X8', 'PAPER_4X8'),
('PAPER_7X4.75', 'PAPER_7X4.75'),
('PAPER_8.27X11.67', 'PAPER_8.27X11.67'),
('PAPER_8.5X11_BOTTOM_HALF_LABEL', 'PAPER_8.5X11_BOTTOM_HALF_LABEL'),
('PAPER_8.5X11', 'PAPER_8.5X11'),
('STOCK_2.4X6', 'STOCK_2.4X6'),
('STOCK_2.9X5', 'STOCK_2.9X5'),
('STOCK_3.8X4.2', 'STOCK_3.8X4.2'),
('STOCK_3.9X7', 'STOCK_3.9X7'),
('STOCK_4X4', 'STOCK_4X4'),
('STOCK_4X6', 'STOCK_4X6'),
('STOCK_4X6.5', 'STOCK_4X6.5'),
('STOCK_4X6.75_LEADING_DOC_TAB', 'STOCK_4X6.75_LEADING_DOC_TAB'),
('STOCK_4X7.5', 'STOCK_4X7.5'),
('STOCK_4X8', 'STOCK_4X8')
]
class DeliverCarrier(models.Model):
_inherit = 'delivery.carrier'
delivery_type = fields.Selection(
selection_add=[('envia', 'Envia')],
ondelete={'envia': lambda recs: recs.write({'delivery_type': 'fixed', 'fixed_price': 0})},
)
envia_production_api_key = fields.Text(
string="Envia Production Access Token",
help="Generate an Access Token from within the Production Portal of Envia",
copy=False, groups="base.group_system",
)
envia_sandbox_api_key = fields.Text(
string="Envia Sandbox Access Token",
help="Generate an Access Token from within the Sandbox Portal of Envia",
copy=False, groups="base.group_system",
)
envia_default_package_type_id = fields.Many2one(
"stock.package.type",
string="Envia Default Package",
domain="[('package_carrier_type', '=', 'envia')]",
help="Envia requires package dimensions for getting accurate rate, "
"you can define these in a package type that you set as default",
)
envia_mail_type = fields.Selection(related='envia_default_package_type_id.envia_mail_type')
envia_carrier_code = fields.Char(
string='Envia.com Carrier Code',
store=True,
help='The carrier on Envia.com used by this carrier. The service code belongs to it.',
compute='_compute_services',
)
envia_service_code = fields.Char(
string='Envia.com Service Code',
store=True,
help='The service that will be used for this carrier. This is set when you select a carrier from the wizard.',
compute='_compute_services',
)
envia_service_name = fields.Char(
string='Envia.com Service Name',
store=True,
help='The service that will be used for this carrier. This is set when you select a carrier from the wizard.',
compute='_compute_services'
)
envia_currency_id = fields.Many2one(
'res.currency',
string="Envia Account Main Currency", copy=False,
default=lambda self: self.env.company.currency_id,
help="Currency set in Envia",
)
envia_label_stock_type = fields.Selection(
selection=ENVIA_STOCK_TYPE,
string='Envia Label Type',
help='Select the size of the label',
default='PAPER_8.5X11',
)
envia_label_file_type = fields.Selection(
selection=[
('PNG', 'PNG'),
('ZPLII', 'ZPLII'),
('EPL', 'EPL'),
('PDF', 'PDF'),
('ZPL', 'ZPL'),
],
string='Envia Label File Type',
help='Select the printing format of the label',
default='PDF',
)
country_id = fields.Many2one(
'res.country',
string='Ship From',
default=lambda self: self.env.company.country_id,
help="Select the country to be used by this delivery method",
)
envia_return_at_senders_expense = fields.Boolean(
string='Returned at Shippers Expense',
default=False,
help='If the carrier is unable to deliver the package, the package can be returned to the shipper or abandoned at the door. (Canada only)',
)
envia_lift_pickup = fields.Boolean(
string='Lift Assistance on Pickup',
default=False,
help='Provide liftgate assitance if the supplier doesn\'t have a dock or forklift to load the shipment. (United States and Mexico Only)',
)
envia_lift_delivery = fields.Boolean(
string='Lift Assistance on Delivery',
default=False,
help='Provide liftgate assistance if the recipient doesn\'t have a dock or forklift to unload the shipment. (United States and Mexico Only)',
)
envia_residential_delivery = fields.Boolean(
string='Delivery Residential Zone',
default=False,
help='Certain carriers like UPS will charge an extra fee to deliver to a residential zone (United States Only)',
)
envia_residential_pickup = fields.Boolean(
string='Pickup Residential Zone',
default=False,
help='Certain carriers like UPS will charge an extra fee to pickup from residential zones (United States Only)',
)
@api.depends('country_id', 'envia_default_package_type_id.envia_mail_type', 'prod_environment')
def _compute_services(self):
""" Each country has different carriers or services
In addition, depending on the mail type different carriers
will have different services.
Swapping to prod also has different supported services and carriers.
"""
for carrier in self:
if carrier.delivery_type == 'envia':
carrier.envia_carrier_code = ""
carrier.envia_service_code = ""
carrier.envia_service_name = ""
def _compute_supports_shipping_insurance(self):
super()._compute_supports_shipping_insurance()
for carrier in self:
if carrier.delivery_type == 'envia':
carrier.supports_shipping_insurance = True
def _envia_convert_weight(self, weight):
""" Returns the weight in KG for a Envia order."""
self.ensure_one()
weight_uom_id = self.env['product.template']._get_weight_uom_id_from_ir_config_parameter()
return weight_uom_id._compute_quantity(weight, self.env.ref('uom.product_uom_kgm'), round=False)
def _envia_convert_size(self, size):
""" Returns the size in CM for a Envia order."""
self.ensure_one()
size_uom_id = self.env['product.template']._get_length_uom_id_from_ir_config_parameter()
return size_uom_id._compute_quantity(size, self.env.ref('uom.product_uom_cm'), round=False)
def action_open_envia_wizard(self):
""" Fetch carriers and channels from Envia account.
create record(s) of carriers(s) in odoo.
"""
self.ensure_one()
if self.delivery_type != 'envia':
raise ValidationError(self.env._('This action requires an Envia.com carrier.'))
envia = Envia(self, self.prod_environment, self.log_xml)
carriers_data = envia._fetch_envia_carriers()
if errors_found := carriers_data.get('error'):
raise ValidationError(errors_found)
carriers_list = carriers_data.get('carriers')
if not carriers_list:
raise ValidationError(self.env._("Failed to fetch Envia Carriers, Please try again later."))
return {
'name': self.env._("Choose an Envia.com Shipping Service"),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'envia.shipping.wizard',
'target': 'new',
'context': {
'default_carrier_id': self.id,
'default_available_services': carriers_list,
'default_selected_service_code': self.envia_service_code,
'default_selected_carrier_code': self.envia_carrier_code,
},
}
def envia_rate_shipment(self, order):
""" Returns shipping rate for the order and chosen shipping method."""
if not self.envia_carrier_code or not self.envia_service_code:
return {
'success': False,
'price': 0.0,
'error_message': self.env._("No carrier is set on \"%(delivery_method)s\". To use Envia.com, you'll need to sync your carriers with your account.", delivery_method=self.name),
'warning_message': False,
}
order_weight = self.env.context.get('order_weight', None)
envia = Envia(self, self.prod_environment, self.log_xml)
result = envia._rate_request(
order.partner_shipping_id,
order.warehouse_id.partner_id or order.warehouse_id.company_id.partner_id,
order,
order_weight=order_weight
)
if result.get('error_found'):
return {
'success': False,
'price': 0.0,
'error_message': result['error_found'],
'warning_message': False
}
price = float(result['price'])
return {
'success': True,
'price': price,
'error_message': False,
'warning_message': result.get('warning_message'),
}
def envia_send_shipping(self, pickings):
""" Send shipment to Envia for validation.
Add shipment to cart, checkout, and generate label.
"""
if not self.envia_carrier_code or not self.envia_service_code:
raise UserError(self.env._("No carrier is set on \"%(delivery_method)s\". To use Envia.com, you'll need to sync your carriers with your account.", delivery_method=self.name))
res = []
envia = Envia(self, self.prod_environment, self.log_xml)
for pick in pickings:
shipment = envia._send_shipping(pick)
res.append({
'tracking_number': shipment.get('tracking_number'),
'exact_price': shipment.get('exact_price')
})
return res
def envia_get_tracking_link(self, picking):
""" Returns the tracking link for a picking."""
if self.prod_environment:
root_url = "https://envia.com"
else:
root_url = "https://dev.envia.com"
params = {'label': picking.carrier_tracking_ref}
return f"{root_url}/tracking?{urlencode(params)}"
def envia_cancel_shipment(self, pickings):
""" Attempts to cancel shipment from within Envias
backend. May run into issues if the shipment has already
shipped or label been picked up by carrier.
"""
envia = Envia(self, self.prod_environment, self.log_xml)
for pick in pickings:
if pick.carrier_id.delivery_type != 'envia' or not pick.carrier_tracking_ref:
pick.message_post(body=pick.env._("Envia order(s) not found to cancel shipment!"))
continue
invalid_trackings = envia._cancel_picking(pick)
if invalid_trackings:
pick.message_post(body=pick.env._("Unable to cancel order: %(order_number)s", order_number=invalid_trackings))
else:
pick.write({
"carrier_tracking_ref": '',
"carrier_price": 0.00,
})