diff --git a/addons/barcodes/models/barcodes.py b/addons/barcodes/models/barcodes.py
index 6c3d228895e..1ee93c3bdd6 100644
--- a/addons/barcodes/models/barcodes.py
+++ b/addons/barcodes/models/barcodes.py
@@ -174,14 +174,14 @@ class BarcodeRule(models.Model):
barcode_nomenclature_id = fields.Many2one('barcode.nomenclature', string='Barcode Nomenclature')
sequence = fields.Integer(string='Sequence', help='Used to order rules such that rules with a smaller sequence match first')
encoding = fields.Selection([
- ('any', _('Any')),
+ ('any', 'Any'),
('ean13', 'EAN-13'),
('ean8', 'EAN-8'),
('upca', 'UPC-A'),
], string='Encoding', required=True, default='any', help='This rule will apply only if the barcode is encoded with the specified encoding')
type = fields.Selection([
- ('alias', _('Alias')),
- ('product', _('Unit Product'))
+ ('alias', 'Alias'),
+ ('product', 'Unit Product')
], string='Type', required=True, default='product')
pattern = fields.Char(string='Barcode Pattern', size=32, help="The barcode matching pattern", required=True, default='.*')
alias = fields.Char(string='Alias', size=32, default='0', help='The matched pattern will alias to this barcode', required=True)
diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py
index 89ea7efcd43..6fd3c5e4e2b 100644
--- a/addons/crm/models/crm_lead.py
+++ b/addons/crm/models/crm_lead.py
@@ -371,52 +371,60 @@ class Lead(models.Model):
for lead in self:
stage_id = lead._stage_find(domain=[('probability', '=', 100.0), ('on_change', '=', True)])
lead.write({'stage_id': stage_id.id, 'probability': 100})
- if lead.user_id and lead.team_id and lead.planned_revenue:
- query = """
- SELECT
- SUM(CASE WHEN user_id = %(user_id)s THEN 1 ELSE 0 END) as total_won,
- MAX(CASE WHEN date_closed >= CURRENT_DATE - INTERVAL '30 days' AND user_id = %(user_id)s THEN planned_revenue ELSE 0 END) as max_user_30,
- MAX(CASE WHEN date_closed >= CURRENT_DATE - INTERVAL '7 days' AND user_id = %(user_id)s THEN planned_revenue ELSE 0 END) as max_user_7,
- MAX(CASE WHEN date_closed >= CURRENT_DATE - INTERVAL '30 days' AND team_id = %(team_id)s THEN planned_revenue ELSE 0 END) as max_team_30,
- MAX(CASE WHEN date_closed >= CURRENT_DATE - INTERVAL '7 days' AND team_id = %(team_id)s THEN planned_revenue ELSE 0 END) as max_team_7
- FROM crm_lead
- WHERE
- type = 'opportunity'
- AND
- active = True
- AND
- probability = 100
- AND
- DATE_TRUNC('year', date_closed) = DATE_TRUNC('year', CURRENT_DATE)
- AND
- (user_id = %(user_id)s OR team_id = %(team_id)s)
- """
- lead.env.cr.execute(query, {'user_id': lead.user_id.id,
- 'team_id': lead.team_id.id})
- query_result = self.env.cr.dictfetchone()
- message = False
- if query_result['total_won'] == 1:
- message = _('Go, go, go! Congrats for your first deal.')
- elif query_result['max_team_30'] == lead.planned_revenue:
- message = _('Boom! Team record for the past 30 days.')
- elif query_result['max_team_7'] == lead.planned_revenue:
- message = _('Yeah! Deal of the last 7 days for the team.')
- elif query_result['max_user_30'] == lead.planned_revenue:
- message = _('You just beat your personal record for the past 30 days.')
- elif query_result['max_user_7'] == lead.planned_revenue:
- message = _('You just beat your personal record for the past 7 days.')
+ return True
- if message:
- return {
- 'effect': {
- 'fadeout': 'slow',
- 'message': message,
- 'img_url': '/web/image/%s/%s/image' % (lead.team_id.user_id._name, lead.team_id.user_id.id) if lead.team_id.user_id.image else '/web/static/src/img/smile.svg',
- 'type': 'rainbow_man',
- }
+ @api.multi
+ def action_set_won_rainbowman(self):
+ self.ensure_one()
+ self.action_set_won()
+
+ if self.user_id and self.team_id and self.planned_revenue:
+ query = """
+ SELECT
+ SUM(CASE WHEN user_id = %(user_id)s THEN 1 ELSE 0 END) as total_won,
+ MAX(CASE WHEN date_closed >= CURRENT_DATE - INTERVAL '30 days' AND user_id = %(user_id)s THEN planned_revenue ELSE 0 END) as max_user_30,
+ MAX(CASE WHEN date_closed >= CURRENT_DATE - INTERVAL '7 days' AND user_id = %(user_id)s THEN planned_revenue ELSE 0 END) as max_user_7,
+ MAX(CASE WHEN date_closed >= CURRENT_DATE - INTERVAL '30 days' AND team_id = %(team_id)s THEN planned_revenue ELSE 0 END) as max_team_30,
+ MAX(CASE WHEN date_closed >= CURRENT_DATE - INTERVAL '7 days' AND team_id = %(team_id)s THEN planned_revenue ELSE 0 END) as max_team_7
+ FROM crm_lead
+ WHERE
+ type = 'opportunity'
+ AND
+ active = True
+ AND
+ probability = 100
+ AND
+ DATE_TRUNC('year', date_closed) = DATE_TRUNC('year', CURRENT_DATE)
+ AND
+ (user_id = %(user_id)s OR team_id = %(team_id)s)
+ """
+ self.env.cr.execute(query, {'user_id': self.user_id.id,
+ 'team_id': self.team_id.id})
+ query_result = self.env.cr.dictfetchone()
+
+ message = False
+ if query_result['total_won'] == 1:
+ message = _('Go, go, go! Congrats for your first deal.')
+ elif query_result['max_team_30'] == self.planned_revenue:
+ message = _('Boom! Team record for the past 30 days.')
+ elif query_result['max_team_7'] == self.planned_revenue:
+ message = _('Yeah! Deal of the last 7 days for the team.')
+ elif query_result['max_user_30'] == self.planned_revenue:
+ message = _('You just beat your personal record for the past 30 days.')
+ elif query_result['max_user_7'] == self.planned_revenue:
+ message = _('You just beat your personal record for the past 7 days.')
+
+ if message:
+ return {
+ 'effect': {
+ 'fadeout': 'slow',
+ 'message': message,
+ 'img_url': '/web/image/%s/%s/image' % (self.team_id.user_id._name, self.team_id.user_id.id) if self.team_id.user_id.image else '/web/static/src/img/smile.svg',
+ 'type': 'rainbow_man',
}
- return True
+ }
+ return True
@api.multi
def action_schedule_meeting(self):
diff --git a/addons/crm/views/crm_lead_views.xml b/addons/crm/views/crm_lead_views.xml
index 994a577239b..07600dc9465 100644
--- a/addons/crm/views/crm_lead_views.xml
+++ b/addons/crm/views/crm_lead_views.xml
@@ -433,7 +433,7 @@
Dotación fundacional
-
+
@@ -14,7 +14,7 @@
1030Fundadores, parte no desembolsada en fundaciones
-
+
@@ -22,7 +22,7 @@
1034Asociados, parte no desembolsada en asociaciones
-
+
@@ -30,7 +30,7 @@
1040Fundadores, por aportaciones no dinerarias pendientes, en fundaciones
-
+
@@ -38,7 +38,7 @@
1044Asociados, por aportaciones no dinerarias pendientes, en asociaciones
-
+
@@ -46,7 +46,7 @@
120Remanente
-
+
@@ -54,7 +54,7 @@
121Resultados negativos de ejercicios anteriores
-
+
@@ -70,7 +70,7 @@
1300Subvenciones del Estado
-
+
@@ -78,7 +78,7 @@
1301Subvenciones de otras Administraciones Públicas
-
+
@@ -86,7 +86,7 @@
1320Otras subvenciones
-
+
@@ -94,7 +94,7 @@
1321Otras donaciones y legados
-
+
diff --git a/addons/l10n_es/data/account_account_template_common_data.xml b/addons/l10n_es/data/account_account_template_common_data.xml
index 1a489efb07d..d7e538bc8b6 100644
--- a/addons/l10n_es/data/account_account_template_common_data.xml
+++ b/addons/l10n_es/data/account_account_template_common_data.xml
@@ -5,7 +5,7 @@
101Fondo social
-
+
@@ -13,7 +13,7 @@
102Capital
-
+
@@ -21,7 +21,7 @@
108Acciones o participaciones propias en situaciones especiales
-
+
@@ -29,7 +29,7 @@
109Acciones o participaciones propias para reducción de capital
-
+
@@ -37,7 +37,7 @@
110Prima de emisión o asunción
-
+
@@ -45,7 +45,7 @@
112Reserva legal
-
+
@@ -53,7 +53,7 @@
113Reservas voluntarias
-
+
@@ -61,7 +61,7 @@
1140Reservas para acciones o participaciones de la sociedad dominante
-
+
@@ -69,7 +69,7 @@
1141Reservas estatutarias
-
+
@@ -77,7 +77,7 @@
1142Reserva por capital amortizado
-
+
@@ -85,7 +85,7 @@
1144Reservas por acciones propias aceptadas en garantía
-
+
@@ -93,7 +93,7 @@
118Aportaciones de socios o propietarios
-
+
@@ -101,7 +101,7 @@
119Diferencias por ajuste del capital a euros
-
+
@@ -109,7 +109,7 @@
131Donaciones y legados de capital
-
+
@@ -117,7 +117,7 @@
1370Ingresos fiscales por diferencias permanentes a distribuir en varios ejercicios
-
+
@@ -125,7 +125,7 @@
1371Ingresos fiscales por deducciones y bonificaciones a distribuir en varios ejercicios
-
+
@@ -133,7 +133,7 @@
141Provisión para impuestos
-
+
@@ -141,7 +141,7 @@
142Provisión para otras responsabilidades
-
+
@@ -149,7 +149,7 @@
143Provisión por desmantelamiento, retiro o rehabilitación del inmovilizado
-
+
@@ -157,7 +157,7 @@
145Provisión para actuaciones medioambientales
-
+
@@ -165,7 +165,7 @@
150Acciones o participaciones consideradas como pasivos financieros
-
+
@@ -173,7 +173,7 @@
1533Desembolsos no exigidos, empresas del grupo
-
+
@@ -181,7 +181,7 @@
1534Desembolsos no exigidos, empresas asociadas
-
+
@@ -189,7 +189,7 @@
1535Desembolsos no exigidos, otras partes vinculadas
-
+
@@ -197,7 +197,7 @@
1536Otros desembolsos no exigidos
-
+
@@ -205,7 +205,7 @@
1543Aportaciones no dinerarias pendientes, empresas del grupo
-
+
@@ -213,7 +213,7 @@
1544Aportaciones no dinerarias pendientes, empresas asociadas
-
+
@@ -221,7 +221,7 @@
1545Aportaciones no dinerarias pendientes, otras partes vinculadas
-
+
@@ -229,7 +229,7 @@
1546Otras aportaciones no dinerarias pendientes
-
+
@@ -237,7 +237,7 @@
1603Deudas con entidades de crédito vinculadas, empresas del grupo
-
+
@@ -245,7 +245,7 @@
1604Deudas con entidades de crédito vinculadas, empresas asociadas
-
+
@@ -253,7 +253,7 @@
1605Deudas con otras entidades de crédito vinculadas
-
+
@@ -261,7 +261,7 @@
1613Proveedores de inmovilizado, empresas del grupo
-
+
@@ -269,7 +269,7 @@
1614Proveedores de inmovilizado, empresas asociadas
-
+
@@ -277,7 +277,7 @@
1615Proveedores de inmovilizado, otras partes vinculadas
-
+
@@ -285,7 +285,7 @@
1623Acreedores por arrendamiento financiero, empresas de grupo
-
+
@@ -293,7 +293,7 @@
1624Acreedores por arrendamiento financiero, empresas asociadas
-
+
@@ -301,7 +301,7 @@
1625Acreedores por arrendamiento financiero, otras partes vinculadas
-
+
@@ -309,7 +309,7 @@
1633Otras deudas, empresas del grupo
-
+
@@ -317,7 +317,7 @@
1634Otras deudas, empresas asociadas
-
+
@@ -325,7 +325,7 @@
1635Otras deudas, con otras partes vinculadas
-
+
@@ -333,7 +333,7 @@
170Deudas con entidades de crédito
-
+
@@ -341,7 +341,7 @@
171Deudas
-
+
@@ -349,7 +349,7 @@
172Deudas transformables en subvenciones, donaciones y legados
-
+
@@ -357,7 +357,7 @@
173Proveedores de inmovilizado
-
+
@@ -365,7 +365,7 @@
174Acreedores por arrendamiento financiero
-
+
@@ -373,7 +373,7 @@
175Efectos a pagar
-
+
@@ -381,7 +381,7 @@
176Pasivos por derivados financieros
-
+
@@ -389,7 +389,7 @@
177Obligaciones y bonos
-
+
@@ -397,7 +397,7 @@
179Deudas representadas en otros valores negociables
-
+
@@ -405,7 +405,7 @@
180Fianzas recibidas
-
+
@@ -413,7 +413,7 @@
181Anticipos recibidos por ventas o prestaciones de servicios
-
+
@@ -421,7 +421,7 @@
185Depósitos recibidos
-
+
@@ -429,7 +429,7 @@
190Acciones o participaciones emitidas
-
+
@@ -437,7 +437,7 @@
192Suscriptores de acciones
-
+
@@ -445,7 +445,7 @@
194Capital emitido pendiente de inscripción
-
+
@@ -453,7 +453,7 @@
195Acciones o participaciones emitidas consideradas como pasivos financieros
-
+
@@ -461,7 +461,7 @@
197Suscriptores de acciones consideradas como pasivos financieros
-
+
@@ -469,7 +469,7 @@
199Acciones o participaciones emitidas consideradas como pasivos financieros pendientes de inscripción
-
+
@@ -1411,9 +1411,9 @@
401
-
+ Proveedores, efectos comerciales a pagar
-
+
@@ -1459,33 +1459,33 @@
404
-
+ Proveedores, empresas asociadas
-
+ 405
-
+ Proveedores, otras partes vinculadas
-
+ 406
-
+ Envases y embalajes a devolver a proveedores
-
+ 407
-
+ Anticipos a proveedores
-
+
@@ -1587,9 +1587,9 @@
432
-
+ Clientes, operaciones de factoring
-
+
@@ -1651,41 +1651,41 @@
434
-
+ Clientes, empresas asociadas
-
+ 435
-
+ Clientes, otras partes vinculadas
-
+ 436
-
+ Clientes de dudoso cobro
-
+ 437
-
+ Envases y embalajes a devolver por clientes
-
+ 438
-
+ Anticipos de clientes
-
+
diff --git a/addons/l10n_es/data/account_account_template_full_data.xml b/addons/l10n_es/data/account_account_template_full_data.xml
index 61d5bc4b0c8..ae643434a38 100644
--- a/addons/l10n_es/data/account_account_template_full_data.xml
+++ b/addons/l10n_es/data/account_account_template_full_data.xml
@@ -6,7 +6,7 @@
100Capital social
-
+
@@ -14,7 +14,7 @@
1030Socios por desembolsos no exigidos, capital social
-
+
@@ -22,7 +22,7 @@
1034Socios por desembolsos no exigidos, capital pendiente de inscripción
-
+
@@ -30,7 +30,7 @@
1040Socios por aportaciones no dinerarias pendientes, capital social
-
+
@@ -38,7 +38,7 @@
1044Socios por aportaciones no dinerarias pendientes, capital pendiente de inscripción
-
+
@@ -46,7 +46,7 @@
1110Patrimonio neto por emision de instrumentos financieros compuestos
-
+
@@ -54,7 +54,7 @@
111100Resto de instrumentos de patrimonio neto
-
+
@@ -62,7 +62,7 @@
1143Reserva por fondo de comercio
-
+
@@ -70,7 +70,7 @@
115Reservas por pérdidas y ganancias actuariales y otros ajustes
-
+
@@ -78,7 +78,7 @@
120Remanente
-
+
@@ -86,7 +86,7 @@
121Resultados negativos de ejercicios anteriores
-
+
@@ -102,7 +102,7 @@
130Subvenciones oficiales de capital
-
+
@@ -110,7 +110,7 @@
132Otras subvenciones, donaciones y legados
-
+
@@ -118,7 +118,7 @@
133Ajustes por valoración en activos financieros disponibles para la venta
-
+
@@ -126,7 +126,7 @@
1340Cobertura de flujos de efectivo
-
+
@@ -134,7 +134,7 @@
1341Cobertura de una inversión neta en un negocio en el extranjero
-
+
@@ -142,7 +142,7 @@
135Diferencias de conversión
-
+
@@ -150,7 +150,7 @@
136Ajustes por valoración de activos no corrientes y grupos enajenables de elementos, mantenidos para la venta
-
+
@@ -158,7 +158,7 @@
140Provisión por retribuciones del personal
-
+
@@ -166,7 +166,7 @@
146Provisión para reestructuraciones
-
+
@@ -174,7 +174,7 @@
147Provisión por transacciones con pagos basados en instrumentos de patrimonio
-
+
@@ -182,7 +182,7 @@
1765Pasivos por derivados financieros, carter de negociación
-
+
@@ -190,7 +190,7 @@
1768Pasivos por derivados financieros, instrumentos de cobertura
-
+
@@ -198,7 +198,7 @@
178Obligaciones y bonos convertibles
-
+
@@ -206,7 +206,7 @@
189Garantías financieras
-
+
diff --git a/addons/l10n_es/data/account_account_template_pymes_data.xml b/addons/l10n_es/data/account_account_template_pymes_data.xml
index 716b61a6752..87fa44cca5c 100644
--- a/addons/l10n_es/data/account_account_template_pymes_data.xml
+++ b/addons/l10n_es/data/account_account_template_pymes_data.xml
@@ -6,7 +6,7 @@
100Capital social
-
+
@@ -14,7 +14,7 @@
1030Socios por desembolsos no exigidos, capital social
-
+
@@ -22,7 +22,7 @@
1034Socios por desembolsos no exigidos, capital pendiente de inscripción
-
+
@@ -30,7 +30,7 @@
1040Socios por aportaciones no dinerarias pendientes, capital social
-
+
@@ -38,7 +38,7 @@
1044Socios por aportaciones no dinerarias pendientes, capital pendiente de inscripción
-
+
@@ -46,7 +46,7 @@
120Remanente
-
+
@@ -54,7 +54,7 @@
121Resultados negativos de ejercicios anteriores
-
+
@@ -70,7 +70,7 @@
130Subvenciones oficiales de capital
-
+
@@ -78,7 +78,7 @@
132Otras subvenciones, donaciones y legados
-
+
diff --git a/addons/l10n_in_hr_payroll/models/l10n_in_hr_payroll.py b/addons/l10n_in_hr_payroll/models/l10n_in_hr_payroll.py
index b52641ec3cb..8421058a60d 100644
--- a/addons/l10n_in_hr_payroll/models/l10n_in_hr_payroll.py
+++ b/addons/l10n_in_hr_payroll/models/l10n_in_hr_payroll.py
@@ -136,7 +136,7 @@ class HrPayslipRun(models.Model):
'company_id': company.id,
'name': run.name,
'date': run.date_end,
- 'bank_id': company.partner_id.bank_ids and company.partner_id.bank_ids[0].id or False
+ 'bank_id': company.partner_id.bank_ids and company.partner_id.bank_ids[0].bank_id.id or False
})
for slip in run.slip_ids:
# TODO is it necessary to interleave the calls ?
diff --git a/addons/l10n_mx/models/account.py b/addons/l10n_mx/models/account.py
index b1c691c85f6..e7df50dbe0b 100644
--- a/addons/l10n_mx/models/account.py
+++ b/addons/l10n_mx/models/account.py
@@ -55,5 +55,5 @@ class AccountAccountTag(models.Model):
_inherit = 'account.account.tag'
nature = fields.Selection([
- ('D', _('Debitable Account')), ('A', _('Creditable Account'))],
+ ('D', 'Debitable Account'), ('A', 'Creditable Account')],
help='Used in Mexican report of electronic accounting (account nature).')
diff --git a/addons/mass_mailing/models/mass_mailing.py b/addons/mass_mailing/models/mass_mailing.py
index 17d6a6f1614..4ac6d3cd4fa 100644
--- a/addons/mass_mailing/models/mass_mailing.py
+++ b/addons/mass_mailing/models/mass_mailing.py
@@ -669,10 +669,11 @@ class MassMailing(models.Model):
"""
else:
query +="""
- AND s.mass_mailing_id = %%(mailing_id)s;
+ AND s.mass_mailing_id = %%(mailing_id)s
+ AND s.model = %%(target_model)s;
"""
query = query % {'target': target._table, 'mail_field': mail_field}
- params = {'mailing_id': self.id, 'mailing_campaign_id': self.mass_mailing_campaign_id.id}
+ params = {'mailing_id': self.id, 'mailing_campaign_id': self.mass_mailing_campaign_id.id, 'target_model': self.mailing_model_real}
self._cr.execute(query, params)
seen_list = set(m[0] for m in self._cr.fetchall())
_logger.info(
diff --git a/addons/point_of_sale/models/barcode_rule.py b/addons/point_of_sale/models/barcode_rule.py
index 9fab9caa69a..5e505983006 100644
--- a/addons/point_of_sale/models/barcode_rule.py
+++ b/addons/point_of_sale/models/barcode_rule.py
@@ -9,9 +9,9 @@ class BarcodeRule(models.Model):
_inherit = 'barcode.rule'
type = fields.Selection(selection_add=[
- ('weight', _('Weighted Product')),
- ('price', _('Priced Product')),
- ('discount', _('Discounted Product')),
- ('client', _('Client')),
- ('cashier', _('Cashier'))
+ ('weight', 'Weighted Product'),
+ ('price', 'Priced Product'),
+ ('discount', 'Discounted Product'),
+ ('client', 'Client'),
+ ('cashier', 'Cashier')
])
diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py
index c791d112fa3..a9842a98ae5 100644
--- a/addons/point_of_sale/models/pos_order.py
+++ b/addons/point_of_sale/models/pos_order.py
@@ -463,6 +463,12 @@ class PosOrder(models.Model):
# for debugging and support purposes
_logger.exception('Reconciliation did not work for order %s', order.name)
+ def _filtered_for_reconciliation(self):
+ filter_states = ['invoiced', 'done']
+ if self.env['ir.config_parameter'].get_param('point_of_sale.order_reconcile_mode', 'all') == 'partner_only':
+ return self.filtered(lambda order: order.state in filter_states and order.partner_id)
+ return self.filtered(lambda order: order.state in filter_states)
+
def _default_session(self):
return self.env['pos.session'].search([('state', '=', 'opened'), ('user_id', '=', self.env.uid)], limit=1)
diff --git a/addons/point_of_sale/models/pos_session.py b/addons/point_of_sale/models/pos_session.py
index 74da78ddf51..48b635ca72b 100644
--- a/addons/point_of_sale/models/pos_session.py
+++ b/addons/point_of_sale/models/pos_session.py
@@ -38,7 +38,7 @@ class PosSession(models.Model):
paid=order.amount_paid,
))
order.action_pos_order_done()
- orders_to_reconcile = session.order_ids.filtered(lambda order: order.state in ['invoiced', 'done'] and order.partner_id)
+ orders_to_reconcile = session.order_ids._filtered_for_reconciliation()
orders_to_reconcile.sudo()._reconcile_payments()
config_id = fields.Many2one(
diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js
index a50ccb14c0e..f0623933788 100644
--- a/addons/point_of_sale/static/src/js/screens.js
+++ b/addons/point_of_sale/static/src/js/screens.js
@@ -399,6 +399,9 @@ var NumpadWidget = PosBaseWidget.extend({
this.$el.find('.mode-button[data-mode="price"]')
.toggleClass('disabled-mode', !has_price_control_rights)
.prop('disabled', !has_price_control_rights);
+ if (!has_price_control_rights && this.state.get('mode')=='price'){
+ this.state.changeMode('quantity');
+ }
},
clickDeleteLastChar: function() {
return this.state.deleteLastChar();
diff --git a/addons/point_of_sale/views/pos_config_view.xml b/addons/point_of_sale/views/pos_config_view.xml
index fd33ecb2fd4..e786caad8d1 100644
--- a/addons/point_of_sale/views/pos_config_view.xml
+++ b/addons/point_of_sale/views/pos_config_view.xml
@@ -160,7 +160,7 @@
-
+
Set barcodes to scan products, customer cards, etc.
@@ -178,7 +178,7 @@
-
+
Choose among several tax regimes when processing an order
@@ -197,7 +197,7 @@
-
+
Use a default specific tax regime
diff --git a/addons/pos_mercury/models/pos_mercury.py b/addons/pos_mercury/models/pos_mercury.py
index eca5ffae0e9..20076cdd692 100644
--- a/addons/pos_mercury/models/pos_mercury.py
+++ b/addons/pos_mercury/models/pos_mercury.py
@@ -13,7 +13,7 @@ class BarcodeRule(models.Model):
_inherit = 'barcode.rule'
type = fields.Selection(selection_add=[
- ('credit', _('Credit Card'))
+ ('credit', 'Credit Card')
])
diff --git a/addons/product/models/product_pricelist.py b/addons/product/models/product_pricelist.py
index 4f14d6394bb..90096f3289a 100644
--- a/addons/product/models/product_pricelist.py
+++ b/addons/product/models/product_pricelist.py
@@ -305,33 +305,42 @@ class Pricelist(models.Model):
:param company_id: if passed, used for looking up properties,
instead of current user's company
"""
+ res = self._get_partner_pricelist_multi([partner_id], company_id)
+ return res[partner_id].id
+
+ def _get_partner_pricelist_multi(self, partner_ids, company_id=None):
+ """ Retrieve the applicable pricelist for given partners in a given company.
+
+ :param company_id: if passed, used for looking up properties,
+ instead of current user's company
+ :return: a dict {partner_id: pricelist}
+ """
Partner = self.env['res.partner']
Property = self.env['ir.property'].with_context(force_company=company_id or self.env.user.company_id.id)
+ Pricelist = self.env['product.pricelist']
- p = Partner.browse(partner_id)
- pl = Property.get('property_product_pricelist', Partner._name, '%s,%s' % (Partner._name, p.id))
- if pl:
- pl = pl[0].id
+ # retrieve values of property
+ result = Property.get_multi('property_product_pricelist', Partner._name, partner_ids)
- if not pl:
- if p.country_id.code:
- pls = self.env['product.pricelist'].search([('country_group_ids.country_ids.code', '=', p.country_id.code)], limit=1)
- pl = pls and pls[0].id
+ remaining_partner_ids = [pid for pid, val in result.items() if not val]
+ if remaining_partner_ids:
+ # get fallback pricelist when no pricelist for a given country
+ pl_fallback = (
+ Pricelist.search([('country_group_ids', '=', False)], limit=1) or
+ Property.get('property_product_pricelist', 'res.partner') or
+ Pricelist.search([], limit=1)
+ )
+ # group partners by country, and find a pricelist for each country
+ domain = [('id', 'in', remaining_partner_ids)]
+ groups = Partner.read_group(domain, ['country_id'], ['country_id'])
+ for group in groups:
+ country_id = group['country_id'] and group['country_id'][0]
+ pl = Pricelist.search([('country_group_ids.country_ids', '=', country_id)], limit=1)
+ pl = pl or pl_fallback
+ for pid in Partner.search(group['__domain']).ids:
+ result[pid] = pl
- if not pl:
- # search pl where no country
- pls = self.env['product.pricelist'].search([('country_group_ids', '=', False)], limit=1)
- pl = pls and pls[0].id
-
- if not pl:
- prop = Property.get('property_product_pricelist', 'res.partner')
- pl = prop and prop[0].id
-
- if not pl:
- pls = self.env['product.pricelist'].search([], limit=1)
- pl = pls and pls[0].id
-
- return pl
+ return result
class ResCountryGroup(models.Model):
diff --git a/addons/product/models/product_template.py b/addons/product/models/product_template.py
index 60ce2ccbe54..3fe64df600a 100644
--- a/addons/product/models/product_template.py
+++ b/addons/product/models/product_template.py
@@ -47,8 +47,8 @@ class ProductTemplate(models.Model):
help="A description of the Product that you want to communicate to your customers. "
"This description will be copied to every Sales Order, Delivery Order and Customer Invoice/Credit Note")
type = fields.Selection([
- ('consu', _('Consumable')),
- ('service', _('Service'))], string='Product Type', default='consu', required=True,
+ ('consu', 'Consumable'),
+ ('service', 'Service')], string='Product Type', default='consu', required=True,
help='A stockable product is a product for which you manage stock. The "Inventory" app has to be installed.\n'
'A consumable product, on the other hand, is a product for which stock is not managed.\n'
'A service is a non-material product you provide.\n'
diff --git a/addons/product/models/res_partner.py b/addons/product/models/res_partner.py
index d307bd6ab09..bb3ead5e878 100644
--- a/addons/product/models/res_partner.py
+++ b/addons/product/models/res_partner.py
@@ -8,17 +8,18 @@ class Partner(models.Model):
_name = 'res.partner'
_inherit = 'res.partner'
+ # NOT A REAL PROPERTY !!!!
property_product_pricelist = fields.Many2one(
'product.pricelist', 'Sale Pricelist', compute='_compute_product_pricelist',
- inverse="_inverse_product_pricelist", company_dependent=False, # NOT A REAL PROPERTY
+ inverse="_inverse_product_pricelist", company_dependent=False,
help="This pricelist will be used, instead of the default one, for sales to the current partner")
@api.multi
@api.depends('country_id')
def _compute_product_pricelist(self):
+ res = self.env['product.pricelist']._get_partner_pricelist_multi(self.ids)
for p in self:
- if not isinstance(p.id, models.NewId): # if not onchange
- p.property_product_pricelist = self.env['product.pricelist']._get_partner_pricelist(p.id)
+ p.property_product_pricelist = res.get(p.id)
@api.one
def _inverse_product_pricelist(self):
diff --git a/addons/project/models/project.py b/addons/project/models/project.py
index 1e88f940a4b..b1db3d087d9 100644
--- a/addons/project/models/project.py
+++ b/addons/project/models/project.py
@@ -203,9 +203,9 @@ class Project(models.Model):
help="Internal email associated with this project. Incoming emails are automatically synchronized "
"with Tasks (or optionally Issues if the Issue Tracker module is installed).")
privacy_visibility = fields.Selection([
- ('followers', _('On invitation only')),
- ('employees', _('Visible by all employees')),
- ('portal', _('Visible by following customers')),
+ ('followers', 'On invitation only'),
+ ('employees', 'Visible by all employees'),
+ ('portal', 'Visible by following customers'),
],
string='Privacy', required=True,
default='employees',
diff --git a/addons/report_intrastat/report/report_intrastat_report.py b/addons/report_intrastat/report/report_intrastat_report.py
index d873c9d3551..2e274b3ec3b 100644
--- a/addons/report_intrastat/report/report_intrastat_report.py
+++ b/addons/report_intrastat/report/report_intrastat_report.py
@@ -66,7 +66,7 @@ class ReportIntrastat(models.Model):
left join report_intrastat_code intrastat on pt.intrastat_id = intrastat.id
left join (res_partner inv_address
left join res_country inv_country on (inv_country.id = inv_address.country_id))
- on (inv_address.id = inv.partner_id)
+ on (inv_address.id = coalesce(inv.partner_shipping_id, inv.partner_id))
where
inv.state in ('open','paid')
and inv_line.product_id is not null
diff --git a/addons/sale/views/sale_views.xml b/addons/sale/views/sale_views.xml
index 1b084c13cb0..b6dbebb1b42 100644
--- a/addons/sale/views/sale_views.xml
+++ b/addons/sale/views/sale_views.xml
@@ -67,6 +67,11 @@
+
diff --git a/addons/sale_crm/views/crm_lead_views.xml b/addons/sale_crm/views/crm_lead_views.xml
index 5bd82a4df22..f602e0d6503 100644
--- a/addons/sale_crm/views/crm_lead_views.xml
+++ b/addons/sale_crm/views/crm_lead_views.xml
@@ -6,7 +6,7 @@
crm.lead
-
+
+
+
+
@@ -32,7 +35,6 @@
-
diff --git a/doc/cla/individual/tpa-odoo.md b/doc/cla/individual/tpa-odoo.md
new file mode 100644
index 00000000000..8d2f5db63bf
--- /dev/null
+++ b/doc/cla/individual/tpa-odoo.md
@@ -0,0 +1,9 @@
+India, 2018-08-31
+
+I hereby agree to the terms of the Odoo Individual Contributor License Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this declaration.
+
+Signed,
+
+Turkesh Patel turkesh4friends@gmail.com https://github.com/tpa-odoo
diff --git a/odoo/addons/base/models/ir_property.py b/odoo/addons/base/models/ir_property.py
index 63ca6d2d6d0..4a7e0d7231b 100644
--- a/odoo/addons/base/models/ir_property.py
+++ b/odoo/addons/base/models/ir_property.py
@@ -159,11 +159,31 @@ class Property(models.Model):
# note: order by 'company_id asc' will return non-null values first
props = self.search(domain, order='company_id asc')
result = {}
- for prop in props:
- # for a given res_id, take the first property only
- id = refs.pop(prop.res_id, None)
- if id is not None:
- result[id] = prop.get_by_record()
+
+ field = self.env[model]._fields[name]
+ if field.type == 'many2one':
+ # optimization for many2one fields
+ Comodel = self.env[field.comodel_name]
+ co_ids = set()
+ for prop in props:
+ # for a given res_id, take the first property only
+ id = refs.pop(prop.res_id, None)
+ if id is not None:
+ val = prop.value_reference
+ if val:
+ val = int(val.split(',')[1])
+ co_ids.add(val)
+ result[id] = val
+ # check for existence in batch, and update result accordingly
+ existing = {rec.id: rec for rec in Comodel.browse(co_ids).exists()}
+ result = {id: existing.get(val, Comodel) for id, val in result.items()}
+
+ else:
+ for prop in props:
+ # for a given res_id, take the first property only
+ id = refs.pop(prop.res_id, None)
+ if id is not None:
+ result[id] = prop.get_by_record()
# set the default value to the ids that are not in result
default_value = result.pop(False, False)
diff --git a/odoo/addons/base/models/res_config.py b/odoo/addons/base/models/res_config.py
index ece9100e2e9..6b84f4f17ae 100644
--- a/odoo/addons/base/models/res_config.py
+++ b/odoo/addons/base/models/res_config.py
@@ -551,14 +551,6 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin)
"""
Set values for the fields other that `default`, `group` and `module`
"""
- pass
-
- @api.multi
- def execute(self):
- self.ensure_one()
- if not self.env.user._is_superuser() and not self.env.user.has_group('base.group_system'):
- raise AccessError(_("Only administrators can change the settings"))
-
self = self.with_context(active_test=False)
classified = self._get_classified_fields()
@@ -608,6 +600,16 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin)
for method in dir(self):
if method.startswith('set_') and method is not 'set_values':
_logger.warning(_('Methods that start with `set_` are deprecated. Override `set_values` instead (Method %s)') % method)
+
+ @api.multi
+ def execute(self):
+ self.ensure_one()
+ if not self.env.user._is_superuser() and not self.env.user.has_group('base.group_system'):
+ raise AccessError(_("Only administrators can change the settings"))
+
+ self = self.with_context(active_test=False)
+ classified = self._get_classified_fields()
+
self.set_values()
# module fields: install/uninstall the selected modules
diff --git a/odoo/addons/test_new_api/models.py b/odoo/addons/test_new_api/models.py
index eb1c6532814..eec467cd7b4 100644
--- a/odoo/addons/test_new_api/models.py
+++ b/odoo/addons/test_new_api/models.py
@@ -388,6 +388,7 @@ class CompanyDependent(models.Model):
_name = 'test_new_api.company'
foo = fields.Char(company_dependent=True)
+ tag_id = fields.Many2one('test_new_api.multi.tag', company_dependent=True)
class CompanyDependentAttribute(models.Model):
diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py
index 1ce7d1a75e2..45815c6f031 100644
--- a/odoo/addons/test_new_api/tests/test_new_fields.py
+++ b/odoo/addons/test_new_api/tests/test_new_fields.py
@@ -647,6 +647,7 @@ class TestFields(common.TransactionCase):
company0 = self.env.ref('base.main_company')
company1 = self.env['res.company'].create({'name': 'A', 'parent_id': company0.id})
company2 = self.env['res.company'].create({'name': 'B', 'parent_id': company1.id})
+
# create one user per company
user0 = self.env['res.users'].create({'name': 'Foo', 'login': 'foo',
'company_id': company0.id, 'company_ids': []})
@@ -654,24 +655,44 @@ class TestFields(common.TransactionCase):
'company_id': company1.id, 'company_ids': []})
user2 = self.env['res.users'].create({'name': 'Baz', 'login': 'baz',
'company_id': company2.id, 'company_ids': []})
- # create a default value for the company-dependent field
- field = self.env['ir.model.fields'].search([('model', '=', 'test_new_api.company'),
- ('name', '=', 'foo')])
- self.env['ir.property'].create({'name': 'foo', 'fields_id': field.id,
+
+ # create values for many2one field
+ tag0 = self.env['test_new_api.multi.tag'].create({'name': 'Qux'})
+ tag1 = self.env['test_new_api.multi.tag'].create({'name': 'Quux'})
+ tag2 = self.env['test_new_api.multi.tag'].create({'name': 'Quuz'})
+
+ # create default values for the company-dependent fields
+ field_foo = self.env['ir.model.fields']._get('test_new_api.company', 'foo')
+ self.env['ir.property'].create({'name': 'foo', 'fields_id': field_foo.id,
'value': 'default', 'type': 'char'})
+ field_tag_id = self.env['ir.model.fields']._get('test_new_api.company', 'tag_id')
+ self.env['ir.property'].create({'name': 'foo', 'fields_id': field_tag_id.id,
+ 'value': tag0, 'type': 'many2one'})
# create/modify a record, and check the value for each user
- record = self.env['test_new_api.company'].create({'foo': 'main'})
+ record = self.env['test_new_api.company'].create({'foo': 'main', 'tag_id': tag1})
record.invalidate_cache()
self.assertEqual(record.sudo(user0).foo, 'main')
self.assertEqual(record.sudo(user1).foo, 'default')
self.assertEqual(record.sudo(user2).foo, 'default')
+ self.assertEqual(record.sudo(user0).tag_id, tag1)
+ self.assertEqual(record.sudo(user1).tag_id, tag0)
+ self.assertEqual(record.sudo(user2).tag_id, tag0)
- record.sudo(user1).foo = 'alpha'
+ record.sudo(user1).write({'foo': 'alpha', 'tag_id': tag2.id})
record.invalidate_cache()
self.assertEqual(record.sudo(user0).foo, 'main')
self.assertEqual(record.sudo(user1).foo, 'alpha')
self.assertEqual(record.sudo(user2).foo, 'default')
+ self.assertEqual(record.sudo(user0).tag_id, tag1)
+ self.assertEqual(record.sudo(user1).tag_id, tag2)
+ self.assertEqual(record.sudo(user2).tag_id, tag0)
+
+ # unlink value of a many2one (tag2), and check again
+ tag2.unlink()
+ self.assertEqual(record.sudo(user0).tag_id, tag1)
+ self.assertEqual(record.sudo(user1).tag_id, tag0.browse())
+ self.assertEqual(record.sudo(user2).tag_id, tag0)
# create company record and attribute
company_record = self.env['test_new_api.company'].create({'foo': 'ABC'})