diff --git a/CHANGELOG b/CHANGELOG index 214f2ab..862af00 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +* Use carrier selection to define the available zips + Version 4.0.0 - 2016-05-03 Version 3.4.0 - 2014-11-05 diff --git a/__init__.py b/__init__.py index a3c764a..8fd5cd9 100644 --- a/__init__.py +++ b/__init__.py @@ -8,7 +8,6 @@ from .sale import * def register(): Pool.register( - CarrierZip, - Carrier, + CarrierSelection, Sale, module='carrier_zip', type_='model') diff --git a/carrier.py b/carrier.py index 12495d7..25fa52c 100644 --- a/carrier.py +++ b/carrier.py @@ -1,51 +1,47 @@ # This file is part of the carrier_zip module for Tryton. # The COPYRIGHT file at the top level of this repository contains the full # copyright notices and license terms. -from trytond.model import ModelView, ModelSQL, fields -from trytond.pool import Pool, PoolMeta +from trytond.model import fields +from trytond.pool import PoolMeta +from trytond.pyson import If, Bool, Eval -__all__ = ['CarrierZip', 'Carrier'] +__all__ = ['CarrierSelection'] -class CarrierZip(ModelSQL, ModelView): - 'Carrier Zip' - __name__ = 'carrier.zip' - carrier = fields.Many2One('carrier', 'Carrier', required=True, select=True) - start_zip = fields.Char('Start Zip', required=True) - end_zip = fields.Char('End Zip', required=True) - - @classmethod - def __setup__(cls): - super(CarrierZip, cls).__setup__() - cls._error_messages.update({ - 'wrong_zip': 'Can\'t validate this zip. You must set it as an ' - 'integer number.' - }) - - @classmethod - def validate(cls, records): - super(CarrierZip, cls).validate(records) - for record in records: - record.check_zip_code() - - def check_zip_code(self): - try: - int(self.start_zip) - int(self.end_zip) - except: - self.raise_user_error('wrong_zip') - - -class Carrier: +class CarrierSelection: + __name__ = 'carrier.selection' __metaclass__ = PoolMeta - __name__ = 'carrier' - zips = fields.One2Many('carrier.zip', 'carrier', 'Carrier Zips') + start_zip = fields.Many2One('country.zip', 'Start Zip', + domain=[ + If(Bool(Eval('to_country')), + ('country', '=', Eval('to_country')), + (), + )], + depends=['to_country']) + end_zip = fields.Many2One('country.zip', 'End Zip', + domain=[ + If(Bool(Eval('to_country')), + ('country', '=', Eval('to_country')), + (), + )], + depends=['to_country']) - @staticmethod - def get_carriers_from_zip(zip_code): - CarrierZip = Pool().get('carrier.zip') - carrier_zips = CarrierZip.search([ - ('start_zip', '<=', zip_code), - ('end_zip', '>=', zip_code), - ]) - return [c.carrier for c in carrier_zips] + def match(self, pattern): + if 'shipment_zip' in pattern: + pattern = pattern.copy() + shipment_zip = pattern.pop('shipment_zip') + if shipment_zip: + start_zip, end_zip = None, None + try: + zip = int(shipment_zip) + if self.start_zip: + start_zip = int(self.start_zip.zip) + if self.end_zip: + end_zip = int(self.end_zip.zip) + except ValueError: + pass + if start_zip and zip < start_zip: + return False + if end_zip and zip > end_zip: + return False + return super(CarrierSelection, self).match(pattern) diff --git a/carrier.xml b/carrier.xml index 35dc53d..e083c85 100644 --- a/carrier.xml +++ b/carrier.xml @@ -4,22 +4,15 @@ The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms. --> - - carrier - - - carrier_form + + carrier.selection + + selection_form - - - carrier.zip - form - carrier_zip_form - - - carrier.zip - tree - carrier_zip_tree + + carrier.selection + + selection_list diff --git a/locale/ca.po b/locale/ca.po new file mode 100644 index 0000000..e2eb4c3 --- /dev/null +++ b/locale/ca.po @@ -0,0 +1,11 @@ +# +msgid "" +msgstr "Content-Type: text/plain; charset=utf-8\n" + +msgctxt "field:carrier.selection,end_zip:" +msgid "End Zip" +msgstr "Codi postal final" + +msgctxt "field:carrier.selection,start_zip:" +msgid "Start Zip" +msgstr "Codi postal inici" diff --git a/locale/ca_ES.po b/locale/ca_ES.po deleted file mode 100644 index 7d106e9..0000000 --- a/locale/ca_ES.po +++ /dev/null @@ -1,64 +0,0 @@ -# -msgid "" -msgstr "Content-Type: text/plain; charset=utf-8\n" - -msgctxt "error:carrier.zip:" -msgid "Can't validate this zip. You must set it as an integer number." -msgstr "" -"No es pot validar aquest codi postal. Heu d'establir-lo com un nombre enter." - -msgctxt "error:sale.sale:" -msgid "The zip \"%s\" is unavailable for the carrier \"%s\"." -msgstr "El codi postal \"%s\" no està disponible per al transportista \"%s\"." - -msgctxt "field:carrier,zips:" -msgid "Carrier Zips" -msgstr "Codis postals transportista" - -msgctxt "field:carrier.zip,carrier:" -msgid "Carrier" -msgstr "Transportista" - -msgctxt "field:carrier.zip,create_date:" -msgid "Create Date" -msgstr "Data creació" - -msgctxt "field:carrier.zip,create_uid:" -msgid "Create User" -msgstr "Usuari creació" - -msgctxt "field:carrier.zip,end_zip:" -msgid "End Zip" -msgstr "Codi postal final" - -msgctxt "field:carrier.zip,id:" -msgid "ID" -msgstr "ID" - -msgctxt "field:carrier.zip,rec_name:" -msgid "Name" -msgstr "Nom" - -msgctxt "field:carrier.zip,start_zip:" -msgid "Start Zip" -msgstr "Codi postal d'inici" - -msgctxt "field:carrier.zip,write_date:" -msgid "Write Date" -msgstr "Data modificació" - -msgctxt "field:carrier.zip,write_uid:" -msgid "Write User" -msgstr "Usuari modificació" - -msgctxt "field:sale.sale,carrier_domain:" -msgid "Carrier Domain" -msgstr "Domini transportista" - -msgctxt "model:carrier.zip,name:" -msgid "Carrier Zip" -msgstr "Codi postal transportista" - -msgctxt "view:carrier.zip:" -msgid "Carrier Zips" -msgstr "Codis postals transportista" diff --git a/locale/es.po b/locale/es.po new file mode 100644 index 0000000..c17c39d --- /dev/null +++ b/locale/es.po @@ -0,0 +1,11 @@ +# +msgid "" +msgstr "Content-Type: text/plain; charset=utf-8\n" + +msgctxt "field:carrier.selection,end_zip:" +msgid "End Zip" +msgstr "Código postal final" + +msgctxt "field:carrier.selection,start_zip:" +msgid "Start Zip" +msgstr "Código postal inicio" diff --git a/locale/es_ES.po b/locale/es_ES.po deleted file mode 100644 index 9998bf9..0000000 --- a/locale/es_ES.po +++ /dev/null @@ -1,64 +0,0 @@ -# -msgid "" -msgstr "Content-Type: text/plain; charset=utf-8\n" - -msgctxt "error:carrier.zip:" -msgid "Can't validate this zip. You must set it as an integer number." -msgstr "" -"No se puede validar este código postal. Establézcalo como un número entero." - -msgctxt "error:sale.sale:" -msgid "The zip \"%s\" is unavailable for the carrier \"%s\"." -msgstr "El código postal \"%s\" no está disponible para el transportista \"%s\"." - -msgctxt "field:carrier,zips:" -msgid "Carrier Zips" -msgstr "Códigos postales transportista" - -msgctxt "field:carrier.zip,carrier:" -msgid "Carrier" -msgstr "Transportista" - -msgctxt "field:carrier.zip,create_date:" -msgid "Create Date" -msgstr "Fecha creación" - -msgctxt "field:carrier.zip,create_uid:" -msgid "Create User" -msgstr "Usuario creación" - -msgctxt "field:carrier.zip,end_zip:" -msgid "End Zip" -msgstr "Código postal final" - -msgctxt "field:carrier.zip,id:" -msgid "ID" -msgstr "ID" - -msgctxt "field:carrier.zip,rec_name:" -msgid "Name" -msgstr "Nombre" - -msgctxt "field:carrier.zip,start_zip:" -msgid "Start Zip" -msgstr "Código postal inicio" - -msgctxt "field:carrier.zip,write_date:" -msgid "Write Date" -msgstr "Fecha modificación" - -msgctxt "field:carrier.zip,write_uid:" -msgid "Write User" -msgstr "Usuario modificación" - -msgctxt "field:sale.sale,carrier_domain:" -msgid "Carrier Domain" -msgstr "Domini transportista" - -msgctxt "model:carrier.zip,name:" -msgid "Carrier Zip" -msgstr "Código postal transportista" - -msgctxt "view:carrier.zip:" -msgid "Carrier Zips" -msgstr "Códigos postales transportista" diff --git a/sale.py b/sale.py index c6ef3bc..4007dd1 100644 --- a/sale.py +++ b/sale.py @@ -1,9 +1,7 @@ # This file is part of the carrier_zip module for Tryton. # The COPYRIGHT file at the top level of this repository contains the full # copyright notices and license terms. -from trytond.model import fields -from trytond.pool import Pool, PoolMeta -from trytond.pyson import Eval +from trytond.pool import PoolMeta from trytond.transaction import Transaction __all__ = ['Sale'] @@ -12,58 +10,12 @@ __all__ = ['Sale'] class Sale: __metaclass__ = PoolMeta __name__ = 'sale.sale' - carrier_domain = fields.Function(fields.One2Many('carrier', None, - 'Carrier Domain', depends=['shipment_address', 'party']), - 'on_change_with_carrier_domain') - @classmethod - def __setup__(cls): - super(Sale, cls).__setup__() - if 'shipment_address' not in cls.lines.on_change: - cls.lines.on_change.add('shipment_address') - if 'shipment_address' not in cls.lines.depends: - cls.lines.depends.append('shipment_address') - cls._error_messages.update({ - 'zip_unavailable': 'The zip "%s" is unavailable for the ' - 'carrier "%s".', - }) - if hasattr(cls, 'carrier'): - if 'shipment_address' not in cls.carrier.on_change: - cls.carrier.on_change.add('shipment_address') - if 'shipment_address' not in cls.carrier.depends: - cls.carrier.depends.append('shipment_address') - carrier_domain = ('id', 'in', Eval('carrier_domain', [])) - if carrier_domain not in cls.carrier.domain: - cls.carrier.domain.append(carrier_domain) - if 'carrier_domain' not in cls.carrier.depends: - cls.carrier.depends.append('carrier_domain') - - @fields.depends('shipment_address', 'party') - def on_change_with_carrier_domain(self, name=None): - Carrier = Pool().get('carrier') - shipment_zip = (self.shipment_address and self.shipment_address.zip - or '') - carrier_ids = [] - carriers = Carrier.search([]) - for carrier in carriers: - for carrier_zip in carrier.zips: - if shipment_zip: - try: - zip_ = int(shipment_zip) - start_zip = int(carrier_zip.start_zip) - end_zip = int(carrier_zip.end_zip) - except: - break - if (start_zip <= zip_ <= end_zip): - carrier_ids.append(carrier.id) - break - else: - carrier_ids.append(carrier.id) - break - else: - if not carrier.zips: - carrier_ids.append(carrier.id) - return carrier_ids + def _get_carrier_selection_pattern(self): + pattern = super(Sale, self)._get_carrier_selection_pattern() + if self.shipment_address: + pattern['shipment_zip'] = self.shipment_address.zip + return pattern def _get_carrier_context(self): context = super(Sale, self)._get_carrier_context() @@ -72,25 +24,6 @@ class Sale: and self.shipment_address.zip or None) return context - def pre_validate(self): - super(Sale, self).pre_validate() - self.check_carrier_zip() - - def check_carrier_zip(self): - shipment_zip = (self.shipment_address and self.shipment_address.zip - or '') - carrier = self.carrier - if not carrier or not carrier.zips: - return - if (carrier and shipment_zip): - for carrier_zip in carrier.zips: - if (int(carrier_zip.start_zip) <= int(shipment_zip) - <= int(carrier_zip.end_zip)): - break - else: - self.raise_user_error('zip_unavailable', - (shipment_zip, carrier.party.rec_name)) - def create_shipment(self, shipment_type): with Transaction().set_context(self._get_carrier_context()): return super(Sale, self).create_shipment(shipment_type) diff --git a/setup.py b/setup.py index 1d16d4b..a658a9a 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,7 @@ for dep in info.get('depends', []): requires.append(get_require_version('trytond')) tests_require = [] -dependency_links = [] +dependency_links = [get_require_version('proteus')] if minor_version % 2: # Add development index for testing with proteus dependency_links.append('https://trydevpi.tryton.org/') @@ -116,4 +116,7 @@ setup(name=name, test_loader='trytond.test_loader:Loader', tests_require=tests_require, use_2to3=True, + convert_2to3_doctests=[ + 'tests/scenario_carrier_zip.rst', + ], ) diff --git a/tests/scenario_carrier_zip.rst b/tests/scenario_carrier_zip.rst new file mode 100644 index 0000000..bed108d --- /dev/null +++ b/tests/scenario_carrier_zip.rst @@ -0,0 +1,126 @@ +==================== +Carrier Zip Scenario +==================== + +Imports:: + + >>> import datetime + >>> from dateutil.relativedelta import relativedelta + >>> from decimal import Decimal + >>> from proteus import Model, Wizard + >>> from trytond.tests.tools import activate_modules + >>> from trytond.modules.company.tests.tools import create_company, \ + ... get_company + >>> from trytond.modules.account.tests.tools import create_fiscalyear, \ + ... create_chart, get_accounts + >>> from trytond.modules.account_invoice.tests.tools import \ + ... set_fiscalyear_invoice_sequences + >>> today = datetime.date.today() + +Install carrier_zip module:: + + >>> config = activate_modules('carrier_zip') + +Create company:: + + >>> _ = create_company() + >>> company = get_company() + +Create fiscal year:: + + >>> fiscalyear = set_fiscalyear_invoice_sequences( + ... create_fiscalyear(company)) + >>> fiscalyear.click('create_period') + +Create chart of accounts:: + + >>> _ = create_chart(company) + >>> accounts = get_accounts(company) + >>> revenue = accounts['revenue'] + >>> expense = accounts['expense'] + +Create some zip codes:: + + >>> Country = Model.get('country.country') + >>> Zip = Model.get('country.zip') + >>> country = Country(name='Country') + >>> country.save() + >>> Zip.save([Zip(country=country, zip=str(i)) for i in range(10)]) + +Create customer:: + + >>> Party = Model.get('party.party') + >>> customer = Party(name='Customer') + >>> address, = customer.addresses + >>> zip, = Zip.find([('zip', '=', '2')]) + >>> address.zip = zip.zip + >>> customer.save() + >>> other_customer = Party(name='Other Customer') + >>> address, = other_customer.addresses + >>> zip, = Zip.find([('zip', '=', '7')]) + >>> address.zip = zip.zip + >>> other_customer.save() + +Create product:: + + >>> ProductUom = Model.get('product.uom') + >>> ProductTemplate = Model.get('product.template') + >>> Product = Model.get('product.product') + >>> unit, = ProductUom.find([('name', '=', 'Unit')]) + >>> product = Product() + >>> template = ProductTemplate() + >>> template.name = 'Product' + >>> template.default_uom = unit + >>> template.type = 'goods' + >>> template.salable = True + >>> template.lead_time = datetime.timedelta(0) + >>> template.list_price = Decimal('20') + >>> template.cost_price = Decimal('8') + >>> template.account_revenue = revenue + >>> template.save() + >>> product.template = template + >>> product.save() + >>> carrier_product = Product() + >>> carrier_template = ProductTemplate() + >>> carrier_template.name = 'Carrier Product' + >>> carrier_template.default_uom = unit + >>> carrier_template.type = 'service' + >>> carrier_template.salable = True + >>> carrier_template.lead_time = datetime.timedelta(0) + >>> carrier_template.list_price = Decimal('3') + >>> carrier_template.cost_price = Decimal(0) + >>> carrier_template.account_revenue = revenue + >>> carrier_template.save() + >>> carrier_product.template = carrier_template + >>> carrier_product.save() + +Create carrier:: + + >>> Carrier = Model.get('carrier') + >>> carrier = Carrier() + >>> party = Party(name='Carrier') + >>> party.save() + >>> carrier.party = party + >>> carrier.carrier_product = carrier_product + >>> carrier.save() + +Create a selection for zips from 1 to 5:: + + >>> CarrierSelection = Model.get('carrier.selection') + >>> csc = CarrierSelection(carrier=carrier) + >>> csc.start_zip, = Zip.find([('zip', '=', '1')]) + >>> csc.end_zip, = Zip.find([('zip', '=', '5')]) + >>> csc.save() + +The carrier is selected for customer:: + + >>> Sale = Model.get('sale.sale') + >>> sale = Sale() + >>> sale.party = customer + >>> sale.carrier == carrier + True + +But it's not selected for customers outside the range:: + + >>> sale.party = other_customer + >>> sale.carrier diff --git a/tests/test_carrier_zip.py b/tests/test_carrier_zip.py index 72d1243..9029132 100644 --- a/tests/test_carrier_zip.py +++ b/tests/test_carrier_zip.py @@ -3,8 +3,12 @@ # The COPYRIGHT file at the top level of this repository contains the full # copyright notices and license terms. import unittest +import doctest + import trytond.tests.test_tryton from trytond.tests.test_tryton import ModuleTestCase +from trytond.tests.test_tryton import doctest_teardown +from trytond.tests.test_tryton import doctest_checker class CarrierZipTestCase(ModuleTestCase): @@ -16,5 +20,9 @@ def suite(): suite = trytond.tests.test_tryton.suite() suite.addTests(unittest.TestLoader().loadTestsFromTestCase( CarrierZipTestCase)) + suite.addTests(doctest.DocFileSuite( + 'scenario_carrier_zip.rst', + tearDown=doctest_teardown, encoding='utf-8', + checker=doctest_checker, + optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)) return suite - diff --git a/tryton.cfg b/tryton.cfg index 2b07617..dfdb415 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -2,6 +2,6 @@ version=4.1.0 depends: carrier - sale + sale_shipment_cost xml: carrier.xml diff --git a/view/carrier_zip_form.xml b/view/carrier_zip_form.xml deleted file mode 100644 index 0800676..0000000 --- a/view/carrier_zip_form.xml +++ /dev/null @@ -1,13 +0,0 @@ - - -
-