Use carrier selection to define the available zips

This commit is contained in:
Sergi Almacellas Abellana 2016-11-28 16:27:11 +01:00
parent f3f8c1410c
commit 76d187811c
17 changed files with 233 additions and 288 deletions

View File

@ -1,3 +1,5 @@
* Use carrier selection to define the available zips
Version 4.0.0 - 2016-05-03 Version 4.0.0 - 2016-05-03
Version 3.4.0 - 2014-11-05 Version 3.4.0 - 2014-11-05

View File

@ -8,7 +8,6 @@ from .sale import *
def register(): def register():
Pool.register( Pool.register(
CarrierZip, CarrierSelection,
Carrier,
Sale, Sale,
module='carrier_zip', type_='model') module='carrier_zip', type_='model')

View File

@ -1,51 +1,47 @@
# This file is part of the carrier_zip module for Tryton. # This file is part of the carrier_zip module for Tryton.
# The COPYRIGHT file at the top level of this repository contains the full # The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms. # copyright notices and license terms.
from trytond.model import ModelView, ModelSQL, fields from trytond.model import fields
from trytond.pool import Pool, PoolMeta from trytond.pool import PoolMeta
from trytond.pyson import If, Bool, Eval
__all__ = ['CarrierZip', 'Carrier'] __all__ = ['CarrierSelection']
class CarrierZip(ModelSQL, ModelView): class CarrierSelection:
'Carrier Zip' __name__ = 'carrier.selection'
__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:
__metaclass__ = PoolMeta __metaclass__ = PoolMeta
__name__ = 'carrier' start_zip = fields.Many2One('country.zip', 'Start Zip',
zips = fields.One2Many('carrier.zip', 'carrier', 'Carrier Zips') 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 match(self, pattern):
def get_carriers_from_zip(zip_code): if 'shipment_zip' in pattern:
CarrierZip = Pool().get('carrier.zip') pattern = pattern.copy()
carrier_zips = CarrierZip.search([ shipment_zip = pattern.pop('shipment_zip')
('start_zip', '<=', zip_code), if shipment_zip:
('end_zip', '>=', zip_code), start_zip, end_zip = None, None
]) try:
return [c.carrier for c in carrier_zips] 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)

View File

@ -4,22 +4,15 @@ The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license terms. --> copyright notices and license terms. -->
<tryton> <tryton>
<data> <data>
<record model="ir.ui.view" id="carrier_view_form"> <record model="ir.ui.view" id="selection_view_form">
<field name="model">carrier</field> <field name="model">carrier.selection</field>
<field name="type" eval="None"/> <field name="inherit" ref="carrier.carrier_selection_view_form"/>
<field name="inherit" ref="carrier.carrier_view_form"/> <field name="name">selection_form</field>
<field name="name">carrier_form</field>
</record> </record>
<record model="ir.ui.view" id="selection_view_list">
<record model="ir.ui.view" id="carrier_zip_view_form"> <field name="model">carrier.selection</field>
<field name="model">carrier.zip</field> <field name="inherit" ref="carrier.carrier_selection_view_list"/>
<field name="type">form</field> <field name="name">selection_list</field>
<field name="name">carrier_zip_form</field>
</record>
<record model="ir.ui.view" id="carrier_zip_view_tree">
<field name="model">carrier.zip</field>
<field name="type">tree</field>
<field name="name">carrier_zip_tree</field>
</record> </record>
</data> </data>

11
locale/ca.po Normal file
View File

@ -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"

View File

@ -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"

11
locale/es.po Normal file
View File

@ -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"

View File

@ -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"

79
sale.py
View File

@ -1,9 +1,7 @@
# This file is part of the carrier_zip module for Tryton. # This file is part of the carrier_zip module for Tryton.
# The COPYRIGHT file at the top level of this repository contains the full # The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms. # copyright notices and license terms.
from trytond.model import fields from trytond.pool import PoolMeta
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval
from trytond.transaction import Transaction from trytond.transaction import Transaction
__all__ = ['Sale'] __all__ = ['Sale']
@ -12,58 +10,12 @@ __all__ = ['Sale']
class Sale: class Sale:
__metaclass__ = PoolMeta __metaclass__ = PoolMeta
__name__ = 'sale.sale' __name__ = 'sale.sale'
carrier_domain = fields.Function(fields.One2Many('carrier', None,
'Carrier Domain', depends=['shipment_address', 'party']),
'on_change_with_carrier_domain')
@classmethod def _get_carrier_selection_pattern(self):
def __setup__(cls): pattern = super(Sale, self)._get_carrier_selection_pattern()
super(Sale, cls).__setup__() if self.shipment_address:
if 'shipment_address' not in cls.lines.on_change: pattern['shipment_zip'] = self.shipment_address.zip
cls.lines.on_change.add('shipment_address') return pattern
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_context(self): def _get_carrier_context(self):
context = super(Sale, self)._get_carrier_context() context = super(Sale, self)._get_carrier_context()
@ -72,25 +24,6 @@ class Sale:
and self.shipment_address.zip or None) and self.shipment_address.zip or None)
return context 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): def create_shipment(self, shipment_type):
with Transaction().set_context(self._get_carrier_context()): with Transaction().set_context(self._get_carrier_context()):
return super(Sale, self).create_shipment(shipment_type) return super(Sale, self).create_shipment(shipment_type)

View File

@ -50,7 +50,7 @@ for dep in info.get('depends', []):
requires.append(get_require_version('trytond')) requires.append(get_require_version('trytond'))
tests_require = [] tests_require = []
dependency_links = [] dependency_links = [get_require_version('proteus')]
if minor_version % 2: if minor_version % 2:
# Add development index for testing with proteus # Add development index for testing with proteus
dependency_links.append('https://trydevpi.tryton.org/') dependency_links.append('https://trydevpi.tryton.org/')
@ -116,4 +116,7 @@ setup(name=name,
test_loader='trytond.test_loader:Loader', test_loader='trytond.test_loader:Loader',
tests_require=tests_require, tests_require=tests_require,
use_2to3=True, use_2to3=True,
convert_2to3_doctests=[
'tests/scenario_carrier_zip.rst',
],
) )

View File

@ -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

View File

@ -3,8 +3,12 @@
# The COPYRIGHT file at the top level of this repository contains the full # The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms. # copyright notices and license terms.
import unittest import unittest
import doctest
import trytond.tests.test_tryton import trytond.tests.test_tryton
from trytond.tests.test_tryton import ModuleTestCase 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): class CarrierZipTestCase(ModuleTestCase):
@ -16,5 +20,9 @@ def suite():
suite = trytond.tests.test_tryton.suite() suite = trytond.tests.test_tryton.suite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase( suite.addTests(unittest.TestLoader().loadTestsFromTestCase(
CarrierZipTestCase)) 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 return suite

View File

@ -2,6 +2,6 @@
version=4.1.0 version=4.1.0
depends: depends:
carrier carrier
sale sale_shipment_cost
xml: xml:
carrier.xml carrier.xml

View File

@ -1,13 +0,0 @@
<?xml version="1.0"?>
<!-- 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. -->
<form string="Carrier Zips" col="6">
<label name="carrier"/>
<field name="carrier"/>
<newline/>
<label name="start_zip"/>
<field name="start_zip"/>
<label name="end_zip"/>
<field name="end_zip"/>
</form>

View File

@ -1,9 +0,0 @@
<?xml version="1.0"?>
<!-- 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. -->
<tree string="Carrier Zips">
<field name="carrier"/>
<field name="start_zip"/>
<field name="end_zip"/>
</tree>

12
view/selection_form.xml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0"?>
<!-- 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. -->
<data>
<xpath expr="/form/field[@name='to_country']" position="after">
<label name="start_zip"/>
<field name="start_zip"/>
<label name="end_zip"/>
<field name="end_zip"/>
</xpath>
</data>

View File

@ -3,7 +3,8 @@
The COPYRIGHT file at the top level of this repository contains the full The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license terms. --> copyright notices and license terms. -->
<data> <data>
<xpath expr="/form/field[@name='carrier_cost_method']" position="after"> <xpath expr="/tree/field[@name='to_country']" position="after">
<field name="zips" colspan="4"/> <field name="start_zip"/>
<field name="end_zip"/>
</xpath> </xpath>
</data> </data>