Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion docsource/modules150-160.rst
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ Module coverage 15.0 -> 16.0
+-------------------------------------------------+----------------------+-------------------------------------------------+
| |new| data_recycle | | |
+-------------------------------------------------+----------------------+-------------------------------------------------+
| delivery | | |
| delivery | Done | |
+-------------------------------------------------+----------------------+-------------------------------------------------+
| delivery_mondialrelay | |No DB layout changes. |
+-------------------------------------------------+----------------------+-------------------------------------------------+
Expand Down
179 changes: 179 additions & 0 deletions openupgrade_scripts/scripts/delivery/16.0.1.0/post-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# SPDX-FileCopyrightText: 2023 Coop IT Easy SC
# SPDX-FileCopyrightText: 2024 Tecnativa - Pedro M. Baeza
# SPDX-License-Identifier: AGPL-3.0-or-later

import logging
import string

from openupgradelib import openupgrade

_logger = logging.getLogger(__name__)

ALPHANUM = string.digits + string.ascii_uppercase


def increment_alphanum(value):
result = []
carry = False
first = True
for digit in reversed(value):
# Preserve spaces and hyphens
if digit in [" ", "-"]:
result.append(digit)
elif digit == "Z" and (first or carry):
result.append("0")
carry = True
elif carry or first:
result.append(ALPHANUM[ALPHANUM.index(digit) + 1])
carry = False
else:
result.append(digit)
first = False
if carry:
result.append("1")
return "".join(result[::-1])


def fill_prefix(prefix, length, item, spaces, dashes):
result = prefix + (length * item)
for index in spaces:
result = result[:index] + " " + result[index + 1 :]
for index in dashes:
result = result[:index] + "-" + result[index + 1 :]
return result


def prefix_works(prefix, start, end, spaces, dashes):
# Get the highest and lowest values for the prefix, and compare them against
# the two extremes.
fill_length = len(start) - len(prefix)
highest = fill_prefix(prefix, fill_length, "Z", spaces, dashes)
lowest = fill_prefix(prefix, fill_length, "0", spaces, dashes)
return lowest >= start and highest <= end


def next_alphanum(prefix, length, end, spaces, dashes):
result = increment_alphanum(prefix)
fill_length = length - len(prefix)
if fill_length:
result = fill_prefix(result, length - len(prefix), "0", spaces, dashes)
if result > end:
return None
return result


def find_occurrences(item, iterable):
return [i for i, val in enumerate(iterable) if val == item]


def range_to_prefixes(start, end):
if len(start) != len(end):
raise ValueError(f"{start!r} and {end!r} do not have an equal length")
# Implementation detail: It is assumed that spaces and dashes occur in
# identical places in start and end. If this is not true, the whole thing
# doesn't work.
spaces = find_occurrences(" ", start)
dashes = find_occurrences("-", start)
if find_occurrences(" ", end) != spaces or find_occurrences("-", end) != dashes:
raise ValueError(
f"{start!r} and {end!r} do not have spaces or dashes in identical"
f" locations"
)
prefixes = set()
not_prefixes = set()
alphanum = start
while alphanum:
prefix = alphanum
candidate = prefix
while prefix:
if prefix in not_prefixes:
break
if prefix_works(prefix, start, end, spaces, dashes):
candidate = prefix
prefix = prefix[:-1]
else:
not_prefixes.add(prefix)
break
prefixes.add(candidate)
alphanum = next_alphanum(candidate, len(start), end, spaces, dashes)
return prefixes


def numerical_range_to_prefixes(min_, max_):
"""Adapted from https://github.com/voronind/range-regex."""

def fill_by_nines(integer, nines_count):
return int(str(integer)[:-nines_count] + "9" * nines_count)

def fill_by_zeros(integer, zeros_count):
return integer - integer % 10**zeros_count

def split_to_ranges(min_, max_):
stops = {max_}
nines_count = 1
stop = fill_by_nines(min_, nines_count)
while min_ <= stop < max_:
stops.add(stop)
nines_count += 1
stop = fill_by_nines(min_, nines_count)
zeros_count = 1
stop = fill_by_zeros(max_ + 1, zeros_count) - 1
while min_ < stop <= max_:
stops.add(stop)
zeros_count += 1
stop = fill_by_zeros(max_ + 1, zeros_count) - 1
stops = list(stops)
stops.sort()
return stops

subpatterns = []
start = min_
for stop in split_to_ranges(min_, max_):
pattern = ""
any_digit_count = 0
for start_digit, stop_digit in zip(str(start), str(stop)):
if start_digit == stop_digit:
pattern += start_digit
elif start_digit != "0" or stop_digit != "9":
pattern += "[{}-{}]".format(start_digit, stop_digit)
else:
any_digit_count += 1
if any_digit_count:
pattern += r"\d"
if any_digit_count > 1:
pattern += "{{{}}}".format(any_digit_count)
subpatterns.append(pattern)
start = stop + 1
return subpatterns


def _convert_carrier_zip_ranges(env):
"""Transform the previous zip_from and zip_to fields to the new prefixes system."""
env.cr.execute(
"SELECT id, zip_from, zip_to FROM delivery_carrier "
"WHERE zip_from IS NOT NULL AND zip_to IS NOT NULL"
)
for carrier_id, zip_from, zip_to in env.cr.fetchall():
if zip_from.isnumeric() and zip_to.isnumeric():
prefixes = numerical_range_to_prefixes(int(zip_from), int(zip_to))
else:
try:
prefixes = range_to_prefixes(zip_from, zip_to)
except Exception as error:
_logger.error(
f"Failed to convert the zip range '{zip_from} --"
f" {zip_to}'of delivery method {carrier_id} to a set of"
f" prefixes. Got error:\n\n{error}"
)
continue
carrier = env["delivery.carrier"].browse(carrier_id)
for prefix in prefixes:
prefix_record = env["delivery.zip.prefix"].search([("name", "=", prefix)])
if not prefix_record:
prefix_record = env["delivery.zip.prefix"].create({"name": prefix})
carrier.zip_prefix_ids |= prefix_record


@openupgrade.migrate()
def migrate(env, version):
_convert_carrier_zip_ranges(env)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---Models in module 'delivery'---
new model delivery.zip.prefix
---Fields in module 'delivery'---
delivery / delivery.carrier / carrier_description (text) : NEW
# NOTHING TO DO: New feature for showing in the sales order a description for the delivery method.

delivery / delivery.carrier / shipping_insurance (integer) : NEW hasdefault: default
# NOTHING TO DO: New feature for indicating to the providers the insurance percentage covered.

delivery / delivery.carrier / zip_from (char) : DEL
delivery / delivery.carrier / zip_to (char) : DEL
delivery / delivery.carrier / zip_prefix_ids (many2many) : NEW relation: delivery.zip.prefix
delivery / delivery.zip.prefix / name (char) : NEW required
# DONE: post-migration: Extract prefixes as regexp.

delivery / product.template / country_of_origin (many2one) : NEW relation: res.country
# NOTHING TO DO: New field for Intrastat. May should be filled from OCA Intrastat modules

delivery / stock.move.line / carrier_name (char) : NEW isrelated: related, stored
# NOTHING TO DO: Related stored already handled in a fast way by ORM.

---XML records in module 'delivery'---
NEW ir.actions.act_window: delivery.action_delivery_zip_prefix_list
NEW ir.model.access: delivery.access_delivery_carrier_system
NEW ir.model.access: delivery.access_delivery_zip_prefix
NEW ir.model.access: delivery.access_delivery_zip_prefix_stock_manager
NEW ir.model.constraint: delivery.constraint_delivery_carrier_shipping_insurance_is_percentage
NEW ir.model.constraint: delivery.constraint_delivery_zip_prefix_name_uniq
NEW ir.ui.menu: delivery.menu_delivery_zip_prefix
NEW ir.ui.view: delivery.delivery_report_saleorder_document
NEW ir.ui.view: delivery.label_package_template_view_delivery
NEW ir.ui.view: delivery.stock_move_line_view_search_delivery
NEW ir.ui.view: delivery.view_picking_type_form_delivery
NEW ir.ui.view: delivery.view_stock_rule_form_delivery
# NOTHING TO DO: noupdate=0 records handled by ORM.