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 3.4.0 - 2014-11-05

View File

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

View File

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

View File

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

View File

@ -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',
],
)

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

View File

@ -2,6 +2,6 @@
version=4.1.0
depends:
carrier
sale
sale_shipment_cost
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
copyright notices and license terms. -->
<data>
<xpath expr="/form/field[@name='carrier_cost_method']" position="after">
<field name="zips" colspan="4"/>
<xpath expr="/tree/field[@name='to_country']" position="after">
<field name="start_zip"/>
<field name="end_zip"/>
</xpath>
</data>