diff --git a/delivery_fedex_hibou/__init__.py b/delivery_fedex_hibou/__init__.py
index 0650744f..09434554 100644
--- a/delivery_fedex_hibou/__init__.py
+++ b/delivery_fedex_hibou/__init__.py
@@ -1 +1,3 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
from . import models
diff --git a/delivery_fedex_hibou/__manifest__.py b/delivery_fedex_hibou/__manifest__.py
index 347e017a..7a99231a 100644
--- a/delivery_fedex_hibou/__manifest__.py
+++ b/delivery_fedex_hibou/__manifest__.py
@@ -1,9 +1,9 @@
{
'name': 'Hibou Fedex Shipping',
- 'version': '13.0.1.0.0',
+ 'version': '13.0.1.1.0',
'category': 'Stock',
'author': "Hibou Corp.",
- 'license': 'AGPL-3',
+ 'license': 'OPL-1',
'website': 'https://hibou.io/',
'depends': [
'delivery_fedex',
diff --git a/delivery_fedex_hibou/models/__init__.py b/delivery_fedex_hibou/models/__init__.py
index cfdf1b44..bad254c1 100644
--- a/delivery_fedex_hibou/models/__init__.py
+++ b/delivery_fedex_hibou/models/__init__.py
@@ -1,2 +1,4 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
from . import delivery_fedex
from . import stock
diff --git a/delivery_fedex_hibou/models/delivery_fedex.py b/delivery_fedex_hibou/models/delivery_fedex.py
index 90544864..21488d24 100644
--- a/delivery_fedex_hibou/models/delivery_fedex.py
+++ b/delivery_fedex_hibou/models/delivery_fedex.py
@@ -1,3 +1,5 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
import logging
import pytz
from odoo import fields, models, tools, _
@@ -223,6 +225,15 @@ class DeliveryFedex(models.Model):
srm = FedexRequest(self.log_xml, request_type="shipping", prod_environment=self.prod_environment)
superself = self.sudo()
+ picking_packages = picking.package_ids
+ package_carriers = picking_packages.mapped('carrier_id')
+ if package_carriers:
+ # only ship ours
+ picking_packages = picking_packages.filtered(lambda p: p.carrier_id == self and not p.carrier_tracking_ref)
+
+ if package_carriers and not picking_packages:
+ continue
+
shipper_company = superself.get_shipper_company(picking=picking)
shipper_warehouse = superself.get_shipper_warehouse(picking=picking)
recipient = superself.get_recipient(picking=picking)
@@ -239,7 +250,7 @@ class DeliveryFedex(models.Model):
srm.transaction_detail(picking.id)
- package_type = picking.package_ids and picking.package_ids[0].packaging_id.shipper_package_code or self.fedex_default_packaging_id.shipper_package_code
+ package_type = picking_packages and picking_packages[0].packaging_id.shipper_package_code or self.fedex_default_packaging_id.shipper_package_code
srm.shipment_request(self.fedex_droppoff_type, self.fedex_service_type, package_type, self.fedex_weight_unit, self.fedex_saturday_delivery)
srm.set_currency(_convert_curr_iso_fdx(picking.company_id.currency_id.name))
srm.set_shipper(shipper_company, shipper_warehouse)
@@ -278,7 +289,7 @@ class DeliveryFedex(models.Model):
send_etd = superself.env['ir.config_parameter'].get_param("delivery_fedex.send_etd")
srm.commercial_invoice(self.fedex_document_stock_type, send_etd)
- package_count = len(picking.package_ids) or 1
+ package_count = len(picking_packages) or 1
# For india picking courier is not accepted without this details in label.
po_number = order.display_name or False
@@ -307,16 +318,17 @@ class DeliveryFedex(models.Model):
package_labels = []
carrier_tracking_ref = ""
- for sequence, package in enumerate(picking.package_ids, start=1):
+ for sequence, package in enumerate(picking_packages, start=1):
package_weight = self._fedex_convert_weight(package.shipping_weight, self.fedex_weight_unit)
packaging = package.packaging_id
+ packaging_code = packaging.shipper_package_code if (packaging.package_carrier_type == 'fedex' and packaging.shipper_package_code) else self.fedex_default_packaging_id.shipper_package_code
# Hibou Delivery
# Add more details to package.
srm._add_package(
package_weight,
- package_code=packaging.shipper_package_code,
+ package_code=packaging_code,
package_height=packaging.height,
package_width=packaging.width,
package_length=packaging.length,
@@ -394,10 +406,11 @@ class DeliveryFedex(models.Model):
# One package #
###############
elif package_count == 1:
- packaging = picking.package_ids[:1].packaging_id or picking.carrier_id.fedex_default_packaging_id
+ packaging = picking_packages[:1].packaging_id or self.fedex_default_packaging_id
+ packaging_code = packaging.shipper_package_code if packaging.package_carrier_type == 'fedex' else self.fedex_default_packaging_id.shipper_package_code
srm._add_package(
net_weight,
- package_code=packaging.shipper_package_code,
+ package_code=packaging_code,
package_height=packaging.height,
package_width=packaging.width,
package_length=packaging.length,
@@ -457,21 +470,33 @@ class DeliveryFedex(models.Model):
picking.message_post(body='Fedex Documents', attachments=fedex_documents)
return res
- def fedex_rate_shipment_multi(self, order=None, picking=None):
+ def fedex_rate_shipment_multi(self, order=None, picking=None, packages=None):
+ if not packages:
+ return self._fedex_rate_shipment_multi_package(order=order, picking=picking)
+ else:
+ rates = []
+ for package in packages:
+ rates += self._fedex_rate_shipment_multi_package(order=order, picking=picking, package=package)
+ return rates
+
+ def _fedex_rate_shipment_multi_package(self, order=None, picking=None, package=None):
if order:
max_weight = self._fedex_convert_weight(self.fedex_default_packaging_id.max_weight, self.fedex_weight_unit)
is_india = order.partner_shipping_id.country_id.code == 'IN' and order.company_id.partner_id.country_id.code == 'IN'
est_weight_value = sum([(line.product_id.weight * line.product_uom_qty) for line in order.order_line]) or 0.0
weight_value = self._fedex_convert_weight(est_weight_value, self.fedex_weight_unit)
order_currency = order.currency_id
- else:
- # max_weight = self._fedex_convert_weight(self.fedex_default_packaging_id.max_weight, self.fedex_weight_unit)
+ elif not package:
is_india = picking.partner_id.country_id.code == 'IN' and picking.company_id.partner_id.country_id.code == 'IN'
- # TODO must be per-package eventually
- # theoretically just sum of all packages weights, but the rating itself will also need to change...
est_weight_value = sum([(line.product_id.weight * (line.qty_done or line.product_uom_qty)) for line in picking.move_line_ids]) or 0.0
weight_value = self._fedex_convert_weight(est_weight_value, self.fedex_weight_unit)
order_currency = picking.sale_id.currency_id if picking.sale_id else picking.company_id.currency_id
+ else:
+ is_india = picking.partner_id.country_id.code == 'IN' and picking.company_id.partner_id.country_id.code == 'IN'
+ order_currency = picking.sale_id.currency_id if picking.sale_id else picking.company_id.currency_id
+ est_weight_value = self._fedex_convert_weight(package.shipping_weight or package.weight)
+ weight_value = self._fedex_convert_weight(est_weight_value, self.fedex_weight_unit)
+
price = 0.0
@@ -558,9 +583,28 @@ class DeliveryFedex(models.Model):
)
srm.set_master_package(weight_value, 1)
else:
- for sequence, package in enumerate(picking.package_ids, start=1):
- package_weight = self._fedex_convert_weight(package.shipping_weight, self.fedex_weight_unit)
+ if package:
+ package_weight = self._fedex_convert_weight(package.shipping_weight or package.weight, self.fedex_weight_unit)
packaging = package.packaging_id
+ package_code = package.packaging_id.shipper_package_code if packaging.package_carrier_type == 'fedex' else self.fedex_default_packaging_id.shipper_package_code
+
+ srm.add_package(
+ package_weight,
+ mode='rating',
+ package_code=package_code,
+ package_height=packaging.height,
+ package_width=packaging.width,
+ package_length=packaging.length,
+ sequence_number=1,
+ # po_number=po_number,
+ # dept_number=dept_number,
+ reference=('%s-%d' % (order_name, 1)),
+ insurance=insurance_value
+ )
+ else:
+ # deliver all together...
+ package_weight = self._fedex_convert_weight(picking.shipping_weight or picking.weight, self.fedex_weight_unit)
+ packaging = self.fedex_default_packaging_id
srm.add_package(
package_weight,
@@ -569,10 +613,10 @@ class DeliveryFedex(models.Model):
package_height=packaging.height,
package_width=packaging.width,
package_length=packaging.length,
- sequence_number=sequence,
+ sequence_number=1,
# po_number=po_number,
# dept_number=dept_number,
- reference=('%s-%d' % (order_name, sequence)),
+ reference=('%s-%d' % (order_name, 1)),
insurance=insurance_value
)
@@ -643,6 +687,7 @@ class DeliveryFedex(models.Model):
tz = pytz.timezone(self.delivery_calendar_id.tz)
date_delivered = tz.localize(date_delivered).astimezone(pytz.utc).replace(tzinfo=None)
result.append({'carrier': carrier,
+ 'package': package or self.env['stock.quant.package'].browse(),
'success': True,
'price': price,
'error_message': False,
diff --git a/delivery_fedex_hibou/models/fedex_request.py b/delivery_fedex_hibou/models/fedex_request.py
index 0a561abf..e5043069 100644
--- a/delivery_fedex_hibou/models/fedex_request.py
+++ b/delivery_fedex_hibou/models/fedex_request.py
@@ -1,3 +1,5 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
from zeep.exceptions import Fault
from datetime import datetime
from copy import deepcopy
diff --git a/delivery_fedex_hibou/models/stock.py b/delivery_fedex_hibou/models/stock.py
index 3dfb1a1f..58726f6f 100644
--- a/delivery_fedex_hibou/models/stock.py
+++ b/delivery_fedex_hibou/models/stock.py
@@ -1,3 +1,5 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
from odoo import api, fields, models
diff --git a/delivery_gso/__init__.py b/delivery_gso/__init__.py
index 0650744f..09434554 100644
--- a/delivery_gso/__init__.py
+++ b/delivery_gso/__init__.py
@@ -1 +1,3 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
from . import models
diff --git a/delivery_gso/__manifest__.py b/delivery_gso/__manifest__.py
index 2856bc7a..dc28415f 100644
--- a/delivery_gso/__manifest__.py
+++ b/delivery_gso/__manifest__.py
@@ -1,10 +1,10 @@
{
'name': 'Golden State Overnight (gso.com) Shipping',
'summary': 'Send your shippings through gso.com and track them online.',
- 'version': '11.0.1.0.0',
+ 'version': '13.0.1.1.0',
'author': "Hibou Corp.",
'category': 'Warehouse',
- 'license': 'AGPL-3',
+ 'license': 'OPL-1',
'images': [],
'website': "https://hibou.io",
'description': """
diff --git a/delivery_gso/models/__init__.py b/delivery_gso/models/__init__.py
index 943392d3..c9a65a9e 100644
--- a/delivery_gso/models/__init__.py
+++ b/delivery_gso/models/__init__.py
@@ -1 +1,3 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
from . import delivery_gso
diff --git a/delivery_gso/models/delivery_gso.py b/delivery_gso/models/delivery_gso.py
index 3727bc80..3d302c4c 100644
--- a/delivery_gso/models/delivery_gso.py
+++ b/delivery_gso/models/delivery_gso.py
@@ -1,3 +1,5 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
import pytz
from math import ceil
from base64 import b64decode
@@ -198,9 +200,15 @@ class ProviderGSO(models.Model):
'thermal': [],
'paper': [],
}
- if picking.package_ids:
+ picking_packages = picking.package_ids
+ package_carriers = picking_packages.mapped('carrier_id')
+ if package_carriers:
+ # only ship ours
+ picking_packages = picking_packages.filtered(lambda p: p.carrier_id == self and not p.carrier_tracking_ref)
+
+ if picking_packages:
# Every package will be a transaction
- for package in picking.package_ids:
+ for package in picking_packages:
request_body['Shipment']['Weight'] = self._gso_convert_weight(package.shipping_weight)
request_body['Shipment'].update(self._gso_get_package_dimensions(package))
request_body['Shipment']['ShipmentReference'] = package.name
@@ -217,7 +225,8 @@ class ProviderGSO(models.Model):
cost += response['ShipmentCharges']['TotalCharge']
except HTTPError as e:
raise ValidationError(e)
- else:
+ elif not package_carriers:
+ # ship the whole picking
request_body['Shipment']['Weight'] = self._gso_convert_weight(picking.shipping_weight)
request_body['Shipment'].update(self._gso_get_package_dimensions())
request_body['Shipment']['ShipmentReference'] = picking.name
@@ -234,6 +243,8 @@ class ProviderGSO(models.Model):
cost += response['ShipmentCharges']['TotalCharge']
except HTTPError as e:
raise ValidationError(e)
+ else:
+ continue
# Handle results
trackings = [l[0] for l in labels['thermal']] + [l[0] for l in labels['paper']]
@@ -331,18 +342,32 @@ class ProviderGSO(models.Model):
res.append('https://www.gso.com/Tracking')
return res
- def gso_rate_shipment_multi(self, order=None, picking=None):
+ def gso_rate_shipment_multi(self, order=None, picking=None, packages=None):
+ if not packages:
+ return self._gso_rate_shipment_multi_package(order=order, picking=picking)
+ else:
+ rates = []
+ for package in packages:
+ rates += self._gso_rate_shipment_multi_package(order=order, picking=picking, package=package)
+ return rates
+
+ def _gso_rate_shipment_multi_package(self, order=None, picking=None, package=None):
sudoself = self.sudo()
- service = sudoself._get_gso_service()
+ try:
+ service = sudoself._get_gso_service()
+ except HTTPError as e:
+ _logger.error(e)
+ return [{
+ 'success': False,
+ 'price': 0.0,
+ 'error_message': _('GSO web service returned an error. ' + str(e)),
+ 'warning_message': False,
+ }]
+
from_ = sudoself.get_shipper_warehouse(order=order, picking=picking)
to = sudoself.get_recipient(order=order, picking=picking)
address_type = 'B' if bool(to.is_company or to.parent_id.is_company) else 'R'
-
- if order:
- est_weight_value = self._gso_convert_weight(
- sum([(line.product_id.weight * line.product_uom_qty) for line in order.order_line]) or 0.0)
- else:
- est_weight_value = self._gso_convert_weight(picking.shipping_weight)
+ package_dimensions = self._gso_get_package_dimensions(package=package)
date_planned = fields.Datetime.now()
if self.env.context.get('date_planned'):
@@ -353,10 +378,13 @@ class ProviderGSO(models.Model):
ship_date_gso = ship_date_utc.astimezone(pytz.timezone(GSO_TZ))
ship_date_gso = fields.Datetime.to_string(ship_date_gso)
- if picking and picking.package_ids:
- package_dimensions = self._gso_get_package_dimensions(package=picking.package_ids[0])
+ if order:
+ est_weight_value = self._gso_convert_weight(
+ sum([(line.product_id.weight * line.product_uom_qty) for line in order.order_line]) or 0.0)
+ elif not package:
+ est_weight_value = self._gso_convert_weight(picking.shipping_weight)
else:
- package_dimensions = self._gso_get_package_dimensions()
+ est_weight_value = self._gso_convert_weight(package.shipping_weight or package.weight)
request_body = {
'AccountNumber': sudoself.gso_account_number,
@@ -395,6 +423,7 @@ class ProviderGSO(models.Model):
if carrier:
rates.append({
'carrier': carrier,
+ 'package': package or self.env['stock.quant.package'].browse(),
'success': True,
'price': price,
'error_message': False,
diff --git a/delivery_gso/models/requests_gso.py b/delivery_gso/models/requests_gso.py
index e5fceb3a..4e512661 100644
--- a/delivery_gso/models/requests_gso.py
+++ b/delivery_gso/models/requests_gso.py
@@ -1,3 +1,5 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
import requests
from json import dumps
diff --git a/delivery_hibou/__manifest__.py b/delivery_hibou/__manifest__.py
index 4897f03c..fedb9c16 100644
--- a/delivery_hibou/__manifest__.py
+++ b/delivery_hibou/__manifest__.py
@@ -1,7 +1,7 @@
{
'name': 'Delivery Hibou',
'summary': 'Adds underlying pinnings for things like "RMA Return Labels"',
- 'version': '13.0.1.0.0',
+ 'version': '13.0.1.1.0',
'author': "Hibou Corp.",
'category': 'Stock',
'license': 'AGPL-3',
diff --git a/delivery_hibou/models/delivery.py b/delivery_hibou/models/delivery.py
index 4b478dd8..d281fb5f 100644
--- a/delivery_hibou/models/delivery.py
+++ b/delivery_hibou/models/delivery.py
@@ -1,5 +1,6 @@
from odoo import fields, models
from odoo.addons.stock.models.stock_move import PROCUREMENT_PRIORITIES
+from odoo.exceptions import UserError
class DeliveryCarrier(models.Model):
@@ -161,11 +162,12 @@ class DeliveryCarrier(models.Model):
# -------------------------- #
# API for external providers #
# -------------------------- #
- def rate_shipment_multi(self, order=None, picking=None):
+ def rate_shipment_multi(self, order=None, picking=None, packages=None):
''' Compute the price of the order shipment
:param order: record of sale.order or None
:param picking: record of stock.picking or None
+ :param packages: recordset of stock.quant.package or None (requires picking also set)
:return list: dict: {
'carrier': delivery.carrier(),
'success': boolean,
@@ -176,6 +178,7 @@ class DeliveryCarrier(models.Model):
'date_delivered': a datetime for when the shipment is supposed to arrive,
'transit_days': a Float for how many days it takes in transit,
'service_code': a string that represents the service level/agreement,
+ 'package': stock.quant.package(),
}
e.g. self == delivery.carrier(5, 6)
@@ -190,12 +193,53 @@ class DeliveryCarrier(models.Model):
if picking:
self = self.with_context(date_planned=fields.Datetime.now())
+ if not packages:
+ packages = picking.package_ids
else:
+ if packages:
+ raise UserError('Cannot rate package without picking.')
self = self.with_context(date_planned=(order.date_planned or fields.Datetime.now()))
res = []
for carrier in self:
+ carrier_packages = packages.filtered(lambda p: not p.carrier_tracking_ref and
+ (not p.carrier_id or p.carrier_id == carrier) and
+ p.packaging_id.package_carrier_type in (False, '', 'none', carrier.delivery_type))
+ if packages and not carrier_packages:
+ continue
if hasattr(carrier, '%s_rate_shipment_multi' % self.delivery_type):
- carrier_rates = getattr(carrier, '%s_rate_shipment_multi' % carrier.delivery_type)(order=order, picking=picking)
- res += carrier_rates
+ try:
+ res += getattr(carrier, '%s_rate_shipment_multi' % carrier.delivery_type)(order=order,
+ picking=picking,
+ packages=carrier_packages)
+ except TypeError:
+ # TODO remove catch if after Odoo 14
+ # This is intended to find ones that don't support packages= kwarg
+ res += getattr(carrier, '%s_rate_shipment_multi' % carrier.delivery_type)(order=order,
+ picking=picking)
+
return res
+
+ def cancel_shipment(self, pickings, packages=None):
+ ''' Cancel a shipment
+
+ :param pickings: A recordset of pickings
+ :param packages: Optional recordset of packages (should be for this carrier)
+ '''
+ self.ensure_one()
+ if hasattr(self, '%s_cancel_shipment' % self.delivery_type):
+ # No good way to tell if this method takes the kwarg for packages
+ if packages:
+ try:
+ return getattr(self, '%s_cancel_shipment' % self.delivery_type)(pickings, packages=packages)
+ except TypeError:
+ # we won't be able to cancel the packages properly
+ # here we will TRY to make a good call here where we put the package references into the picking
+ # and let the original mechanisms try to work here
+ tracking_ref = ','.join(packages.mapped('carrier_tracking_ref'))
+ pickings.write({
+ 'carrier_id': self.id,
+ 'carrier_tracking_ref': tracking_ref,
+ })
+
+ return getattr(self, '%s_cancel_shipment' % self.delivery_type)(pickings)
diff --git a/delivery_hibou/models/stock.py b/delivery_hibou/models/stock.py
index 35559223..9258b4e8 100644
--- a/delivery_hibou/models/stock.py
+++ b/delivery_hibou/models/stock.py
@@ -1,4 +1,27 @@
-from odoo import api, fields, models
+from odoo import api, fields, models, _
+from odoo.exceptions import UserError
+
+
+class StockQuantPackage(models.Model):
+ _inherit = 'stock.quant.package'
+
+ carrier_id = fields.Many2one('delivery.carrier', string='Carrier')
+ carrier_tracking_ref = fields.Char(string='Tracking Reference')
+
+ def _get_active_picking(self):
+ picking_id = self._context.get('active_id')
+ picking_model = self._context.get('active_model')
+ if not picking_id or picking_model != 'stock.picking':
+ raise UserError('Cannot cancel package other than through shipment/picking.')
+ return self.env['stock.picking'].browse(picking_id)
+
+ def send_to_shipper(self):
+ picking = self._get_active_picking()
+ picking.with_context(packages=self).send_to_shipper()
+
+ def cancel_shipment(self):
+ picking = self._get_active_picking()
+ picking.with_context(packages=self).cancel_shipment()
class StockPicking(models.Model):
@@ -11,6 +34,22 @@ class StockPicking(models.Model):
('no', 'No'),
], string='Require Insurance', default='auto',
help='If your carrier supports it, auto should be calculated off of the "Automatic Insurance Value" field.')
+ package_carrier_tracking_ref = fields.Char(string='Package Tracking Numbers', compute='_compute_package_carrier_tracking_ref')
+
+ @api.depends('package_ids.carrier_tracking_ref')
+ def _compute_package_carrier_tracking_ref(self):
+ for picking in self:
+ package_refs = picking.package_ids.filtered('carrier_tracking_ref').mapped('carrier_tracking_ref')
+ if package_refs:
+ picking.package_carrier_tracking_ref = ','.join(package_refs)
+ else:
+ picking.package_carrier_tracking_ref = False
+
+ @api.onchange('carrier_id')
+ def _onchange_carrier_id_for_priority(self):
+ for picking in self:
+ if picking.carrier_id and picking.carrier_id.procurement_priority:
+ picking.priority = picking.carrier_id.procurement_priority
@api.depends('move_lines.priority', 'carrier_id')
def _compute_priority(self):
@@ -26,6 +65,11 @@ class StockPicking(models.Model):
so = self.env['sale.order'].search([('name', '=', str(origin))], limit=1)
if so and so.shipping_account_id:
values['shipping_account_id'] = so.shipping_account_id.id
+ carrier_id = values.get('carrier_id')
+ if carrier_id:
+ carrier = self.env['delivery.carrier'].browse(carrier_id)
+ if carrier.procurement_priority:
+ values['priority'] = carrier.procurement_priority
res = super(StockPicking, self).create(values)
return res
@@ -38,6 +82,85 @@ class StockPicking(models.Model):
cost = sum([(l.product_id.standard_price * l.product_uom_qty) for l in self.move_lines] or [0.0])
return cost
+ def clear_carrier_tracking_ref(self):
+ self.write({'carrier_tracking_ref': False})
+
+ def reset_carrier_tracking_ref(self):
+ for picking in self:
+ picking.carrier_tracking_ref = picking.package_carrier_tracking_ref
+
+ # Override to send to specific packaging carriers
+ def send_to_shipper(self):
+ packages = self._context.get('packages')
+ self.ensure_one()
+ if not packages:
+ packages = self.package_ids
+ package_carriers = packages.mapped('carrier_id')
+ if not package_carriers:
+ # Original behavior
+ return super().send_to_shipper()
+
+ tracking_numbers = []
+ carrier_prices = []
+ order_currency = self.sale_id.currency_id or self.company_id.currency_id
+ for carrier in package_carriers:
+ self.carrier_id = carrier
+ carrier_packages = packages.filtered(lambda p: p.carrier_id == carrier)
+ res = carrier.send_shipping(self)
+ if res:
+ res = res[0]
+ if carrier.free_over and self.sale_id and self.sale_id._compute_amount_total_without_delivery() >= carrier.amount:
+ res['exact_price'] = 0.0
+ carrier_price = res['exact_price'] * (1.0 + (self.carrier_id.margin / 100.0))
+ carrier_prices.append(carrier_price)
+ tracking_number = ''
+ if res['tracking_number']:
+ tracking_number = res['tracking_number']
+ tracking_numbers.append(tracking_number)
+ # Try to add tracking to the individual packages.
+ potential_tracking_numbers = tracking_number.split(',')
+ if len(potential_tracking_numbers) >= len(carrier_packages):
+ for t, p in zip(potential_tracking_numbers, carrier_packages):
+ p.carrier_tracking_ref = t
+ else:
+ carrier_packages.write({'carrier_tracking_ref': tracking_number})
+ msg = _("Shipment sent to carrier %s for shipping with tracking number %s Cost: %.2f %s") % (carrier.name, tracking_number, carrier_price, order_currency.name)
+ self.message_post(body=msg)
+
+ self.carrier_price = sum(carrier_prices or [0.0])
+ self.carrier_tracking_ref = ','.join(tracking_numbers or [''])
+ self._add_delivery_cost_to_so()
+
+ # Override to provide per-package versions...
+ def cancel_shipment(self):
+ packages = self._context.get('packages')
+ pickings_with_package_tracking = self.filtered(lambda p: p.package_carrier_tracking_ref)
+ for picking in pickings_with_package_tracking:
+ if packages:
+ current_packages = packages
+ else:
+ current_packages = picking.package_ids
+ # Packages without a carrier can just be cleared
+ packages_without_carrier = current_packages.filtered(lambda p: not p.carrier_id and p.carrier_tracking_ref)
+ packages_without_carrier.write({
+ 'carrier_tracking_ref': False,
+ })
+ # Packages with carrier can use the carrier method
+ packages_with_carrier = current_packages.filtered(lambda p: p.carrier_id and p.carrier_tracking_ref)
+ carriers = packages_with_carrier.mapped('carrier_id')
+ for carrier in carriers:
+ carrier_packages = packages_with_carrier.filtered(lambda p: p.carrier_id == carrier)
+ carrier.cancel_shipment(self, packages=carrier_packages)
+ package_refs = ','.join(carrier_packages.mapped('carrier_tracking_ref'))
+ msg = "Shipment %s cancelled" % package_refs
+ picking.message_post(body=msg)
+ carrier_packages.write({'carrier_tracking_ref': False})
+
+ pickings_without_package_tracking = self - pickings_with_package_tracking
+ if pickings_without_package_tracking:
+ # use original on these
+ super(StockPicking, pickings_without_package_tracking).cancel_shipment()
+
class StockMove(models.Model):
_inherit = 'stock.move'
diff --git a/delivery_hibou/tests/test_delivery_hibou.py b/delivery_hibou/tests/test_delivery_hibou.py
index 7b47b750..2fc53309 100644
--- a/delivery_hibou/tests/test_delivery_hibou.py
+++ b/delivery_hibou/tests/test_delivery_hibou.py
@@ -24,11 +24,11 @@ class TestDeliveryHibou(common.TransactionCase):
def test_delivery_hibou(self):
# Assign a new shipping account
- self.partner.shipping_account_id = self.shipping_account
+ self.partner.shipping_account_ids = self.shipping_account
# Assign values to new Carrier
test_insurance_value = 600
- test_procurement_priority = '2'
+ test_procurement_priority = '1'
self.carrier.automatic_insurance_value = test_insurance_value
self.carrier.procurement_priority = test_procurement_priority
@@ -135,9 +135,9 @@ class TestDeliveryHibou(common.TransactionCase):
picking_in.carrier_id = self.carrier
# This relies heavily on the 'stock' demo data.
# Should only have a single move_line_ids and it should not be done at all.
- self.assertEqual(picking_in.move_line_ids.mapped('qty_done'), [0.0])
- self.assertEqual(picking_in.move_line_ids.mapped('product_uom_qty'), [35.0])
- self.assertEqual(picking_in.move_line_ids.mapped('product_id.standard_price'), [55.0])
+ self.assertEqual(picking_in.move_line_ids.mapped('qty_done'), [0.0, 0.0, 0.0])
+ self.assertEqual(picking_in.move_line_ids.mapped('product_uom_qty'), [35.0, 10.0, 12.0])
+ self.assertEqual(picking_in.move_line_ids.mapped('product_id.standard_price'), [55.0, 35.0, 1700.0])
self.assertEqual(picking_in.carrier_id._classify_picking(picking=picking_in), 'in')
self.assertEqual(picking_in.carrier_id.get_shipper_company(picking=picking_in),
diff --git a/delivery_hibou/views/delivery_views.xml b/delivery_hibou/views/delivery_views.xml
index 01208bbd..638ef921 100644
--- a/delivery_hibou/views/delivery_views.xml
+++ b/delivery_hibou/views/delivery_views.xml
@@ -11,4 +11,16 @@
+
+
+ hibou.choose.delivery.package.form
+ choose.delivery.package
+
+
+
+ [('product_id', '=', False)]
+
+
+
+
\ No newline at end of file
diff --git a/delivery_hibou/views/stock_views.xml b/delivery_hibou/views/stock_views.xml
index 60cfdef9..f806127d 100644
--- a/delivery_hibou/views/stock_views.xml
+++ b/delivery_hibou/views/stock_views.xml
@@ -1,16 +1,48 @@
+
+ hibou.stock.quant.package.form
+ stock.quant.package
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
hibou.delivery.stock.picking_withcarrier.form.viewstock.picking
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/delivery_stamps/__manifest__.py b/delivery_stamps/__manifest__.py
index 382e8d60..e10a2eff 100644
--- a/delivery_stamps/__manifest__.py
+++ b/delivery_stamps/__manifest__.py
@@ -3,7 +3,7 @@
{
'name': 'Stamps.com (USPS) Shipping',
'summary': 'Send your shippings through Stamps.com and track them online.',
- 'version': '13.0.1.0.0',
+ 'version': '13.0.1.1.0',
'author': "Hibou Corp.",
'category': 'Warehouse',
'license': 'OPL-1',
diff --git a/delivery_stamps/models/delivery_stamps.py b/delivery_stamps/models/delivery_stamps.py
index c9a1829d..9f1c284a 100644
--- a/delivery_stamps/models/delivery_stamps.py
+++ b/delivery_stamps/models/delivery_stamps.py
@@ -145,11 +145,13 @@ class ProviderStamps(models.Model):
ret_val.ContentType = self._stamps_content_type()
return ret_val
- def _get_stamps_shipping_multi(self, service, date_planned, order=False, picking=False):
+ def _get_stamps_shipping_multi(self, service, date_planned, order=False, picking=False, package=False):
if order:
weight = sum([(line.product_id.weight * line.product_qty) for line in order.order_line]) or 0.0
- else:
+ elif not package:
weight = picking.shipping_weight
+ else:
+ weight = package.shipping_weight or package.weight
weight = self._stamps_convert_weight(weight)
shipper = self.get_shipper_warehouse(order=order, picking=picking)
@@ -162,7 +164,7 @@ class ProviderStamps(models.Model):
ret_val.ShipDate = date_planned.strftime('%Y-%m-%d') if date_planned else date.today().isoformat()
ret_val.From = self._stamps_address(service, shipper)
ret_val.To = self._stamps_address(service, recipient)
- ret_val.PackageType = self._stamps_package_type()
+ ret_val.PackageType = self._stamps_package_type(package=package)
ret_val.WeightLb = weight
ret_val.ContentType = 'Merchandise'
return ret_val
@@ -204,7 +206,13 @@ class ProviderStamps(models.Model):
if not all((from_partner.zip, to_partner.zip)):
raise ValidationError('Stamps needs ZIP/PostalCode. From: ' + str(from_partner.zip) + ' To: ' + str(to_partner.zip))
- for package in picking.package_ids:
+ picking_packages = picking.package_ids
+ package_carriers = picking_packages.mapped('carrier_id')
+ if package_carriers:
+ # only ship ours
+ picking_packages = picking_packages.filtered(lambda p: p.carrier_id == self and not p.carrier_tracking_ref)
+
+ for package in picking_packages:
weight = self._stamps_convert_weight(package.shipping_weight)
l, w, h = self._stamps_package_dimensions(package=package)
@@ -221,7 +229,7 @@ class ProviderStamps(models.Model):
ret_val.WeightLb = weight
ret_val.ContentType = self._stamps_content_type(package=package)
ret.append((package.name + ret_val.ShipDate + str(ret_val.WeightLb) + self._stamps_hash_partner(to_partner), ret_val))
- if not ret:
+ if not ret and not package_carriers:
weight = self._stamps_convert_weight(picking.shipping_weight)
l, w, h = self._stamps_package_dimensions()
@@ -310,6 +318,8 @@ class ProviderStamps(models.Model):
package_labels = []
shippings = self._stamps_get_shippings_for_picking(service, picking)
+ if not shippings:
+ continue
company, from_partner, to_partner = self._stamps_get_addresses_for_picking(picking)
customs = None
@@ -451,14 +461,23 @@ class ProviderStamps(models.Model):
except WebFault as e:
raise ValidationError(e)
- def stamps_rate_shipment_multi(self, order=None, picking=None):
+ def stamps_rate_shipment_multi(self, order=None, picking=None, packages=None):
+ if not packages:
+ return self._stamps_rate_shipment_multi_package(order=order, picking=picking)
+ else:
+ rates = []
+ for package in packages:
+ rates += self._stamps_rate_shipment_multi_package(order=order, picking=picking, package=package)
+ return rates
+
+ def _stamps_rate_shipment_multi_package(self, order=None, picking=None, package=None):
self.ensure_one()
date_planned = fields.Datetime.now()
if self.env.context.get('date_planned'):
date_planned = self.env.context.get('date_planned')
res = []
service = self._get_stamps_service()
- shipping = self._get_stamps_shipping_multi(service, date_planned, order=order, picking=picking)
+ shipping = self._get_stamps_shipping_multi(service, date_planned, order=order, picking=picking, package=package)
rates = service.get_rates(shipping)
for rate in rates:
price = float(rate.Amount)
@@ -484,6 +503,7 @@ class ProviderStamps(models.Model):
if carrier:
res.append({
'carrier': carrier,
+ 'package': package or self.env['stock.quant.package'].browse(),
'success': True,
'price': price,
'error_message': False,
diff --git a/delivery_stamps/views/delivery_stamps_view.xml b/delivery_stamps/views/delivery_stamps_view.xml
index 4320c0e2..3508b348 100644
--- a/delivery_stamps/views/delivery_stamps_view.xml
+++ b/delivery_stamps/views/delivery_stamps_view.xml
@@ -32,7 +32,7 @@
product.packaging
-
+
diff --git a/delivery_ups_hibou/__init__.py b/delivery_ups_hibou/__init__.py
index 0650744f..09434554 100644
--- a/delivery_ups_hibou/__init__.py
+++ b/delivery_ups_hibou/__init__.py
@@ -1 +1,3 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
from . import models
diff --git a/delivery_ups_hibou/__manifest__.py b/delivery_ups_hibou/__manifest__.py
index c37b2f8b..d65a33ed 100644
--- a/delivery_ups_hibou/__manifest__.py
+++ b/delivery_ups_hibou/__manifest__.py
@@ -1,9 +1,9 @@
{
'name': 'Hibou UPS Shipping',
- 'version': '13.0.1.0.0',
+ 'version': '13.0.1.1.0',
'category': 'Stock',
'author': "Hibou Corp.",
- 'license': 'AGPL-3',
+ 'license': 'OPL-1',
'website': 'https://hibou.io/',
'depends': [
'delivery_ups',
diff --git a/delivery_ups_hibou/models/__init__.py b/delivery_ups_hibou/models/__init__.py
index 735db17b..102ebacd 100644
--- a/delivery_ups_hibou/models/__init__.py
+++ b/delivery_ups_hibou/models/__init__.py
@@ -1,2 +1,4 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
from . import delivery_ups
from . import ups_request_patch
diff --git a/delivery_ups_hibou/models/delivery_ups.py b/delivery_ups_hibou/models/delivery_ups.py
index 7d3ad40f..35ec5529 100644
--- a/delivery_ups_hibou/models/delivery_ups.py
+++ b/delivery_ups_hibou/models/delivery_ups.py
@@ -1,3 +1,5 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError
from odoo.addons.delivery_ups.models.ups_request import UPSRequest, Package
@@ -223,7 +225,17 @@ class ProviderUPS(models.Model):
self.ups_get_return_label(picking)
return res
- def ups_rate_shipment_multi(self, order=None, picking=None):
+ def ups_rate_shipment_multi(self, order=None, picking=None, packages=None):
+ if not packages:
+ return self._ups_rate_shipment_multi_package(order=order, picking=picking)
+ else:
+ rates = []
+ for package in packages:
+ rates += self._ups_rate_shipment_multi_package(order=order, picking=picking, package=package)
+ return rates
+
+ def _ups_rate_shipment_multi_package(self, order=None, picking=None, package=None):
+ # TODO package here is ignored, it should not be (UPS is not multi-rating capable until we can get rates for a single package)
superself = self.sudo()
srm = UPSRequest(self.log_xml, superself.ups_username, superself.ups_passwd, superself.ups_shipper_number, superself.ups_access_number, self.prod_environment)
ResCurrency = self.env['res.currency']
diff --git a/delivery_ups_hibou/models/ups_request_patch.py b/delivery_ups_hibou/models/ups_request_patch.py
index abe84a74..0215aa0c 100644
--- a/delivery_ups_hibou/models/ups_request_patch.py
+++ b/delivery_ups_hibou/models/ups_request_patch.py
@@ -1,3 +1,5 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
from zeep.exceptions import Fault
from odoo.addons.delivery_ups.models.ups_request import UPSRequest
import logging
diff --git a/stock_delivery_planner/__init__.py b/stock_delivery_planner/__init__.py
index 9b429614..c7120225 100644
--- a/stock_delivery_planner/__init__.py
+++ b/stock_delivery_planner/__init__.py
@@ -1,2 +1,4 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
from . import models
from . import wizard
diff --git a/stock_delivery_planner/__manifest__.py b/stock_delivery_planner/__manifest__.py
index a0f55a2c..63470a8d 100644
--- a/stock_delivery_planner/__manifest__.py
+++ b/stock_delivery_planner/__manifest__.py
@@ -1,10 +1,10 @@
{
'name': 'Stock Delivery Planner',
'summary': 'Get rates and choose carrier for delivery.',
- 'version': '13.0.1.0.0',
+ 'version': '13.0.1.1.1',
'author': "Hibou Corp.",
'category': 'Warehouse',
- 'license': 'AGPL-3',
+ 'license': 'OPL-1',
'website': "https://hibou.io",
'description': """
Stock Delivery Planner
@@ -19,6 +19,8 @@ Re-rate deliveries at packing time to find lowest-priced delivery method that st
'stock',
],
'data': [
+ 'security/ir.model.access.csv',
+ 'views/res_config_settings_views.xml',
'views/stock_views.xml',
'wizard/stock_delivery_planner_views.xml',
],
diff --git a/stock_delivery_planner/models/__init__.py b/stock_delivery_planner/models/__init__.py
index 12bab770..03ea18f8 100644
--- a/stock_delivery_planner/models/__init__.py
+++ b/stock_delivery_planner/models/__init__.py
@@ -1 +1,4 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from . import res_config_settings
from . import stock
diff --git a/stock_delivery_planner/models/res_config_settings.py b/stock_delivery_planner/models/res_config_settings.py
new file mode 100644
index 00000000..8e581549
--- /dev/null
+++ b/stock_delivery_planner/models/res_config_settings.py
@@ -0,0 +1,43 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+
+class ResConfigSettings(models.TransientModel):
+ _inherit = 'res.config.settings'
+
+ stock_delivery_planner_base_carrier_ids = fields.Many2many('delivery.carrier',
+ string='Delivery Planner Base Carriers',
+ compute='_compute_stock_delivery_planner_base_carrier_ids',
+ inverse='_inverse_stock_delivery_planner_base_carrier_ids')
+
+ def _compute_stock_delivery_planner_base_carrier_ids_ids(self):
+ # used to compute the field and update in get_values
+ get_param = self.env['ir.config_parameter'].sudo().get_param
+ company_id = self.company_id.id or self.env.company.id
+ carrier_ids = get_param('stock.delivery.planner.carrier_ids.%s' % (company_id,)) or []
+ if carrier_ids and isinstance(carrier_ids, str):
+ try:
+ carrier_ids = [int(c) for c in carrier_ids.split(',')]
+ except:
+ carrier_ids = []
+ return carrier_ids
+
+ def _compute_stock_delivery_planner_base_carrier_ids(self):
+ for settings in self:
+ carrier_ids = settings._compute_stock_delivery_planner_base_carrier_ids_ids()
+ carriers = self.env['delivery.carrier'].browse(carrier_ids)
+ settings.stock_delivery_planner_base_carrier_ids = carriers
+
+ def _inverse_stock_delivery_planner_base_carrier_ids(self):
+ set_param = self.env['ir.config_parameter'].sudo().set_param
+ company_id = self.company_id.id or self.env.company.id
+ for settings in self:
+ carrier_ids = ','.join(str(i) for i in settings.stock_delivery_planner_base_carrier_ids.ids)
+ set_param('stock.delivery.planner.carrier_ids.%s' % (company_id, ), carrier_ids)
+
+ @api.model
+ def get_values(self):
+ res = super(ResConfigSettings, self).get_values()
+ res['stock_delivery_planner_base_carrier_ids'] = [(6, 0, self._compute_stock_delivery_planner_base_carrier_ids_ids())]
+ return res
diff --git a/stock_delivery_planner/models/stock.py b/stock_delivery_planner/models/stock.py
index 5a14cc9b..a3d728af 100644
--- a/stock_delivery_planner/models/stock.py
+++ b/stock_delivery_planner/models/stock.py
@@ -1,3 +1,5 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
from odoo import fields, models, tools, _
from odoo.exceptions import UserError
diff --git a/stock_delivery_planner/security/ir.model.access.csv b/stock_delivery_planner/security/ir.model.access.csv
new file mode 100644
index 00000000..204f355c
--- /dev/null
+++ b/stock_delivery_planner/security/ir.model.access.csv
@@ -0,0 +1,3 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_stock_delivery_planner_user,stock.delivery.planner user,model_stock_delivery_planner,stock.group_stock_user,1,1,1,0
+access_stock_delivery_planner_option_user,stock.delivery.planner.option user,model_stock_delivery_planner_option,stock.group_stock_user,1,1,1,0
\ No newline at end of file
diff --git a/stock_delivery_planner/tests/test_stock_delivery_planner.py b/stock_delivery_planner/tests/test_stock_delivery_planner.py
index c0a5c431..dfcfd049 100644
--- a/stock_delivery_planner/tests/test_stock_delivery_planner.py
+++ b/stock_delivery_planner/tests/test_stock_delivery_planner.py
@@ -1,3 +1,5 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
from odoo import fields
from odoo.tests.common import Form, TransactionCase
@@ -15,8 +17,8 @@ class TestStockDeliveryPlanner(TransactionCase):
self.skipTest('FedEx Shipping Connector demo data is required to run this test.')
self.env['ir.config_parameter'].sudo().set_param('sale.order.planner.carrier_domain',
"[('id', 'in', (%d,))]" % self.fedex_ground.id)
- self.env['ir.config_parameter'].sudo().set_param('stock.delivery.planner.carrier_domain',
- "[('id', 'in', (%d,))]" % self.fedex_ground.id)
+ self.env['ir.config_parameter'].sudo().set_param('stock.delivery.planner.carrier_ids.%s' % (self.env.company.id, ),
+ "%d" % self.fedex_ground.id)
# Does it make sense to set default package in fedex_rate_shipment_multi
# instead of relying on a correctly configured delivery method?
self.fedex_package = self.browse_ref('delivery_fedex.fedex_packaging_FEDEX_25KG_BOX')
@@ -114,11 +116,11 @@ class TestStockDeliveryPlanner(TransactionCase):
self.assertEqual(self.picking.shipping_weight, 0.0)
self.picking.move_line_ids.filtered(lambda ml: ml.product_id == self.product).qty_done = 5.0
- packing_action = self.picking.put_in_pack()
+ packing_action = self.picking.action_put_in_pack()
packing_wizard = Form(self.env[packing_action['res_model']].with_context(packing_action['context']))
packing_wizard.delivery_packaging_id = self.fedex_package
choose_delivery_package = packing_wizard.save()
- choose_delivery_package.put_in_pack()
+ choose_delivery_package.action_put_in_pack()
self.assertEqual(self.picking.shipping_weight, 5.0)
action = self.picking.action_plan_delivery()
diff --git a/stock_delivery_planner/views/res_config_settings_views.xml b/stock_delivery_planner/views/res_config_settings_views.xml
new file mode 100644
index 00000000..bc8f2557
--- /dev/null
+++ b/stock_delivery_planner/views/res_config_settings_views.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ res.config.settings.view.form.inherit
+ res.config.settings
+
+
+
+
+
Delivery Planner
+
+
+
+
+
+ Add a carrier that represents the 'base rate' for a carrier's type.
+ For example, you should add 1 FedEx carrier here and let us build up the
+ rates for your other FedEx shipping methods.
+
+
+
+
+
+
+
+
+
diff --git a/stock_delivery_planner/views/stock_views.xml b/stock_delivery_planner/views/stock_views.xml
index 985dbd14..a65334dd 100644
--- a/stock_delivery_planner/views/stock_views.xml
+++ b/stock_delivery_planner/views/stock_views.xml
@@ -6,7 +6,7 @@
-
+
diff --git a/stock_delivery_planner/wizard/stock_delivery_planner.py b/stock_delivery_planner/wizard/stock_delivery_planner.py
index 448cb475..e18bd3f3 100644
--- a/stock_delivery_planner/wizard/stock_delivery_planner.py
+++ b/stock_delivery_planner/wizard/stock_delivery_planner.py
@@ -1,4 +1,7 @@
-from odoo import api, fields, models, tools
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from odoo import api, fields, models, _
+from odoo.tools import safe_eval
import logging
_logger = logging.getLogger(__name__)
@@ -9,14 +12,28 @@ class StockDeliveryPlanner(models.TransientModel):
picking_id = fields.Many2one('stock.picking', 'Transfer')
plan_option_ids = fields.One2many('stock.delivery.planner.option', 'plan_id', 'Options')
+ packages_planned = fields.Boolean(compute='_compute_packages_planned')
+
+ @api.depends('plan_option_ids.selection')
+ def _compute_packages_planned(self):
+ for wiz in self:
+ packages = wiz.picking_id.package_ids
+ if not packages:
+ wiz.packages_planned = False
+ selected_options = wiz.plan_option_ids.filtered(lambda p: p.selection == 'selected')
+ wiz.packages_planned = len(selected_options) == len(packages)
def create(self, values):
planner = super(StockDeliveryPlanner, self).create(values)
base_carriers = self.env['delivery.carrier']
- carrier_domain = self.env['ir.config_parameter'].sudo().get_param('stock.delivery.planner.carrier_domain')
- if carrier_domain:
- base_carriers = base_carriers.search(tools.safe_eval(carrier_domain))
+ carrier_ids = self.env['ir.config_parameter'].sudo().get_param('stock.delivery.planner.carrier_ids.%s' % (self.env.company.id, ))
+ if carrier_ids:
+ try:
+ carrier_ids = [int(c) for c in carrier_ids.split(',')]
+ base_carriers = base_carriers.browse(carrier_ids)
+ except:
+ pass
for carrier in base_carriers:
rates = carrier.rate_shipment_multi(picking=planner.picking_id)
@@ -24,9 +41,12 @@ class StockDeliveryPlanner(models.TransientModel):
_logger.warning(rate.get('error_message'))
for rate in filter(lambda r: r.get('success'), rates):
rate = self.calculate_delivery_window(rate)
+ # added late in API dev cycle
+ package = rate.get('package') or self.env['stock.quant.package'].browse()
planner.plan_option_ids |= planner.plan_option_ids.create({
'plan_id': self.id,
'carrier_id': rate['carrier'].id,
+ 'package_id': package.id,
'price': rate['price'],
'date_planned': rate['date_planned'],
'requested_date': rate['date_delivered'],
@@ -47,6 +67,12 @@ class StockDeliveryPlanner(models.TransientModel):
rate['date_delivered'] = carrier.calculate_date_delivered(date_planned, rate.get('transit_days'))
return rate
+ def action_plan(self):
+ self.ensure_one()
+ selected_package_options = self.plan_option_ids.filtered(lambda o: o.package_id and o.selection == 'selected')
+ selected_package_options._plan()
+ return {"type": "ir.actions.act_window_close"}
+
class StockDeliveryOption(models.TransientModel):
_name = 'stock.delivery.planner.option'
@@ -54,17 +80,45 @@ class StockDeliveryOption(models.TransientModel):
plan_id = fields.Many2one('stock.delivery.planner', 'Plan', ondelete='cascade')
carrier_id = fields.Many2one('delivery.carrier', 'Delivery Method')
+ package_id = fields.Many2one('stock.quant.package', 'Package')
price = fields.Float('Shipping Price')
date_planned = fields.Datetime('Planned Date')
requested_date = fields.Datetime('Expected Delivery Date')
transit_days = fields.Integer('Transit Days')
sale_requested_date = fields.Datetime('Sale Order Delivery Date', related='plan_id.picking_id.sale_id.requested_date')
days_different = fields.Float('Days Different', compute='_compute_days_different') # use carrier calendar
+ selection = fields.Selection([
+ ('', 'None'),
+ ('selected', 'Selected'),
+ ('deselected', 'De-selected')
+ ])
+
+ def _plan(self):
+ # this is intended to be used during selecting a whole plan
+ for option in self:
+ option.package_id.write({
+ 'carrier_id': option.carrier_id.id,
+ })
def select_plan(self):
- for option in self.filtered('carrier_id'):
- option.plan_id.picking_id.carrier_id = option.carrier_id
- return
+ self.ensure_one()
+ self.selection = 'selected'
+ if self.package_id:
+ # need to deselect other options for this package
+ deselected = self.plan_id.plan_option_ids.filtered(lambda o: o.package_id == self.package_id and o != self)
+ deselected.write({'selection': 'deselected'})
+ return {
+ 'name': _('Delivery Rate Planner'),
+ 'type': 'ir.actions.act_window',
+ 'view_mode': 'form',
+ 'res_model': 'stock.delivery.planner',
+ 'res_id': self.plan_id.id,
+ 'target': 'new',
+ }
+ else:
+ # Select plan for whole shipment
+ self.plan_id.picking_id.carrier_id = self.carrier_id
+ return {"type": "ir.actions.act_window_close"}
@api.depends('requested_date', 'sale_requested_date', 'carrier_id')
def _compute_days_different(self):
diff --git a/stock_delivery_planner/wizard/stock_delivery_planner_views.xml b/stock_delivery_planner/wizard/stock_delivery_planner_views.xml
index c7e26afd..ac373043 100644
--- a/stock_delivery_planner/wizard/stock_delivery_planner_views.xml
+++ b/stock_delivery_planner/wizard/stock_delivery_planner_views.xml
@@ -6,22 +6,33 @@
form