[MERGE] forward port branch saas-12.2 up to 18c735361a

This commit is contained in:
Christophe Simonis
2019-04-01 16:20:00 +02:00
48 changed files with 608 additions and 230 deletions
+18 -9
View File
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from collections import OrderedDict
import json
import re
import uuid
@@ -539,10 +540,7 @@ class AccountInvoice(models.Model):
if not vals.get('journal_id') and vals.get('type'):
vals['journal_id'] = self.with_context(type=vals.get('type'))._default_journal().id
onchanges = {
'_onchange_partner_id': ['account_id', 'payment_term_id', 'fiscal_position_id', 'partner_bank_id'],
'_onchange_journal_id': ['currency_id'],
}
onchanges = self._get_onchange_create()
for onchange_method, changed_fields in onchanges.items():
if any(f not in vals for f in changed_fields):
invoice = self.new(vals)
@@ -696,7 +694,7 @@ class AccountInvoice(models.Model):
"""
# Split `From` and `CC` email address from received email to look for related partners to subscribe on the invoice
subscribed_emails = email_split((msg_dict.get('from') or '') + ',' + (msg_dict.get('cc') or ''))
subscribed_partner_ids = [pid for pid in self._find_partner_from_emails(subscribed_emails) if pid]
seen_partner_ids = [pid for pid in self._find_partner_from_emails(subscribed_emails) if pid]
# Detection of the partner_id of the invoice:
# 1) check if the email_from correspond to a supplier
@@ -720,7 +718,7 @@ class AccountInvoice(models.Model):
# If the partner_id can be found, subscribe it to the bill, otherwise it's left empty to be manually filled
if partner_id:
subscribed_partner_ids.append(partner_id)
seen_partner_ids.append(partner_id)
# Find the right purchase journal based on the "TO" email address
destination_emails = email_split((msg_dict.get('to') or '') + ',' + (msg_dict.get('cc') or ''))
@@ -736,9 +734,13 @@ class AccountInvoice(models.Model):
# Passing `type` in context so that _default_journal(...) can correctly set journal for new vendor bill
invoice = super(AccountInvoice, self.with_context(type=values.get('type'))).message_new(msg_dict, values)
# Subscribe people on the newly created bill
if subscribed_partner_ids:
invoice.message_subscribe(subscribed_partner_ids)
# Subscribe internal users on the newly created bill
partners = self.env['res.partner'].browse(seen_partner_ids)
is_internal = lambda p: (p.user_ids and
all(p.user_ids.mapped(lambda u: u.user_has_groups('base.group_user'))))
partners_to_subscribe = partners.filtered(is_internal)
if partners_to_subscribe:
invoice.message_subscribe([p.id for p in partners_to_subscribe])
return invoice
@api.model
@@ -1657,6 +1659,13 @@ class AccountInvoice(models.Model):
def _get_intrastat_country_id(self):
return self.partner_id.country_id.id
def _get_onchange_create(self):
return OrderedDict([
('_onchange_partner_id', ['account_id', 'payment_term_id', 'fiscal_position_id', 'partner_bank_id']),
('_onchange_journal_id', ['currency_id']),
])
class AccountInvoiceLine(models.Model):
_name = "account.invoice.line"
_description = "Invoice Line"
@@ -391,10 +391,10 @@ class account_journal(models.Model):
action_name = 'action_view_bank_statement_tree'
elif self.type == 'sale':
action_name = 'action_invoice_tree1'
self = self.with_context(use_domain=[('type', 'in', ['out_invoice', 'out_refund'])])
self = self.with_context(use_domain=[('journal_id', '=', self.id)])
elif self.type == 'purchase':
action_name = 'action_vendor_bill_template'
self = self.with_context(use_domain=[('type', 'in', ['in_invoice', 'in_refund'])])
self = self.with_context(use_domain=[('journal_id', '=', self.id)])
else:
action_name = 'action_move_journal_line'
+1 -1
View File
@@ -1086,7 +1086,7 @@ class AccountMoveLine(models.Model):
writeoff_moves += writeoff_move
# writeoff_move.post()
line_to_reconcile += writeoff_move.line_ids.filtered(lambda r: r.account_id == self[0].account_id)
line_to_reconcile += writeoff_move.line_ids.filtered(lambda r: r.account_id == self[0].account_id).sorted(key='id')[-1:]
if writeoff_moves:
writeoff_moves.post()
# Return the writeoff move.line which is to be reconciled
+1 -3
View File
@@ -665,9 +665,7 @@ class account_payment(models.Model):
if any(len(record.invoice_ids) != 1 for record in self):
# For multiple invoices, there is account.register.payments wizard
raise UserError(_("This method should only be called to process a single invoice's payment."))
res = self.post()
self.mapped('payment_transaction_id').filtered(lambda x: x.state == 'done' and not x.is_processed)._post_process_after_done()
return res
return self.post()
def _create_payment_entry(self, amount):
""" Create a journal entry corresponding to a payment, if the payment references invoice(s) they are reconciled.
@@ -418,6 +418,9 @@ class AccountReconcileModel(models.Model):
aml.date_maturity AS aml_date_maturity,
aml.amount_residual AS aml_amount_residual,
aml.amount_residual_currency AS aml_amount_residual_currency,
aml.balance AS aml_balance,
aml.amount_currency AS aml_amount_currency,
account.internal_type AS account_internal_type,
-- Determine a matching or not with the statement line communication using the move.name or move.ref.
regexp_split_to_array(TRIM(REGEXP_REPLACE(move.name, '[^0-9|^\s]', '', 'g')),'\s+')
@@ -565,9 +568,12 @@ class AccountReconcileModel(models.Model):
return True
# Match total residual amount.
total_residual = sum(
aml['aml_currency_id'] and aml['aml_amount_residual_currency'] or aml['aml_amount_residual'] for aml in
candidates)
total_residual = 0.0
for aml in candidates:
if aml['account_internal_type'] == 'liquidity':
total_residual += aml['aml_currency_id'] and aml['aml_amount_currency'] or aml['aml_balance']
else:
total_residual += aml['aml_currency_id'] and aml['aml_amount_residual_currency'] or aml['aml_amount_residual']
line_residual = statement_line.currency_id and statement_line.amount_currency or statement_line.amount
line_currency = statement_line.currency_id or statement_line.journal_id.currency_id or statement_line.company_id.currency_id
@@ -471,6 +471,67 @@ class TestReconciliation(AccountingTestCase):
self.assertEquals(aml.amount_residual, 0, 'The journal item should be totally reconciled')
self.assertEquals(aml.amount_residual_currency, 0, 'The journal item should be totally reconciled')
def test_manual_reconcile_wizard_same_account(self):
move_ids = self.env['account.move']
debit_line_vals = {
'name': '1',
'debit': 728.35,
'credit': 0.0,
'account_id': self.account_rcv.id,
'amount_currency': 795.05,
'currency_id': self.currency_swiss_id,
}
credit_line_vals = {
'name': '1',
'debit': 0.0,
'credit': 728.35,
'account_id': self.account_rsa.id,
'amount_currency': -795.05,
'currency_id': self.currency_swiss_id,
}
vals = {
'journal_id': self.bank_journal_euro.id,
'date': time.strftime('%Y') + '-02-15',
'line_ids': [(0,0, debit_line_vals), (0, 0, credit_line_vals)]
}
move_ids += self.env['account.move'].create(vals)
debit_line_vals = {
'name': '2',
'debit': 0.0,
'credit': 737.10,
'account_id': self.account_rcv.id,
'amount_currency': -811.25,
'currency_id': self.currency_swiss_id,
}
credit_line_vals = {
'name': '2',
'debit': 737.10,
'credit': 0.0,
'account_id': self.account_rsa.id,
'amount_currency': 811.25,
'currency_id': self.currency_swiss_id,
}
vals = {
'journal_id': self.bank_journal_euro.id,
'date': time.strftime('%Y') + '-07-15',
'line_ids': [(0,0, debit_line_vals), (0, 0, credit_line_vals)]
}
move_ids += self.env['account.move'].create(vals)
account_move_line = move_ids.mapped('line_ids').filtered(lambda l: l.account_id == self.account_rcv)
writeoff_vals = [{
'account_id': self.account_rcv.id,
'journal_id': self.bank_journal_euro.id,
'date': time.strftime('%Y') + '-04-15',
'debit': 8.75,
'credit': 0.0
}]
writeoff_line = account_move_line._create_writeoff(writeoff_vals)
(account_move_line + writeoff_line).reconcile()
self.assertEquals(len(writeoff_line), 1, "The writeoff_line (balance_line) should have only one moves line")
self.assertTrue(all(l.reconciled for l in writeoff_line), 'The balance lines should be totally reconciled')
self.assertTrue(all(l.reconciled for l in account_move_line), 'The move lines should be totally reconciled')
def test_reconcile_bank_statement_with_payment_and_writeoff(self):
# Use case:
# Company is in EUR, create a bill for 80 USD and register payment of 80 USD.
@@ -24,8 +24,10 @@ class TestReconciliationMatchingRules(AccountingTestCase):
lines = invoice.move_id.line_ids
return lines.filtered(lambda l: l.account_id == invoice.account_id)
def _check_statement_matching(self, rules, expected_values):
statement_lines = (self.bank_st + self.cash_st).mapped('line_ids')
def _check_statement_matching(self, rules, expected_values, statements=None):
if statements is None:
statements = self.bank_st + self.cash_st
statement_lines = statements.mapped('line_ids')
matching_values = rules._apply_rules(statement_lines)
for st_line_id, values in matching_values.items():
values.pop('reconciled_lines', None)
@@ -34,6 +36,10 @@ class TestReconciliationMatchingRules(AccountingTestCase):
def setUp(self):
super(AccountingTestCase, self).setUp()
self.account_pay = self.env['account.account'].search([('internal_type', '=', 'payable')], limit=1)
self.account_liq = self.env['account.account'].search([('internal_type', '=', 'liquidity')], limit=1)
self.account_rcv = self.env['account.account'].search([('internal_type', '=', 'receivable')], limit=1)
self.partner_1 = self.env['res.partner'].create({'name': 'partner_1'})
self.partner_2 = self.env['res.partner'].create({'name': 'partner_2'})
@@ -45,7 +51,8 @@ class TestReconciliationMatchingRules(AccountingTestCase):
current_assets_account = self.env['account.account'].search(
[('user_type_id', '=', self.env.ref('account.data_account_type_current_assets').id)], limit=1)
self.rule_1 = self.env.ref('account.reconciliation_model_default_rule')
self.rule_0 = self.env.ref('account.reconciliation_model_default_rule')
self.rule_1 = self.rule_0.copy()
self.rule_1.account_id = current_assets_account
self.rule_1.match_partner = True
self.rule_1.match_partner_ids |= self.partner_1 + self.partner_2
@@ -59,10 +66,10 @@ class TestReconciliationMatchingRules(AccountingTestCase):
invoice_number = self.invoice_line_1.move_id.name
bank_journal = self.env['account.journal'].search([('type', '=', 'bank')], limit=1)
self.bank_journal = self.env['account.journal'].search([('type', '=', 'bank')], limit=1)
self.bank_st = self.env['account.bank.statement'].create({
'name': 'test bank journal', 'journal_id': bank_journal.id,
'name': 'test bank journal', 'journal_id': self.bank_journal.id,
})
self.bank_line_1 = self.env['account.bank.statement.line'].create({
'statement_id': self.bank_st.id,
@@ -296,3 +303,47 @@ class TestReconciliationMatchingRules(AccountingTestCase):
{'partner_id': self.partner_2.id, 'debit': 0.0, 'credit': 1000.0},
{'partner_id': self.partner_2.id, 'debit': 1000.0, 'credit': 0.0},
])
def test_reverted_move_matching(self):
AccountMove = self.env['account.move']
move = AccountMove.create({
'name': 'To Revert',
'journal_id': self.bank_journal.id,
})
partner = self.env['res.partner'].create({'name': 'Eugene'})
AccountMoveLine = self.env['account.move.line'].with_context(check_move_validity=False)
payment_payable_line = AccountMoveLine.create({
'account_id': self.account_pay.id,
'move_id': move.id,
'partner_id': partner.id,
'name': 'One of these days',
'debit': 10,
})
payment_bnk_line = AccountMoveLine.create({
'account_id': self.account_liq.id,
'move_id': move.id,
'partner_id': partner.id,
'name': 'I\'m gonna cut you into little pieces',
'credit': 10,
})
move.post()
move_reversed = AccountMove.browse(move.reverse_moves())
self.assertTrue(move_reversed.exists())
bank_st = self.env['account.bank.statement'].create({
'name': 'test bank journal', 'journal_id': self.bank_journal.id,
})
bank_line_1 = self.env['account.bank.statement.line'].create({
'statement_id': bank_st.id,
'name': '8',
'partner_id': partner.id,
'amount': -10,
'sequence': 1,
})
expected_values = {
bank_line_1.id: {'aml_ids': [payment_bnk_line.id], 'model': self.rule_0}
}
self._check_statement_matching(self.rule_0, expected_values, statements=bank_st)
+22 -17
View File
@@ -51,29 +51,26 @@
<table class="table table-sm o_main_table" name="invoice_line_table">
<thead>
<tr>
<t t-set="colspan" t-value="6"/>
<th class="text-left"><span>Description</span></th>
<th class="d-none text-left"><span>Source Document</span></th>
<th class="text-right"><span>Quantity</span></th>
<th t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"><span>Unit Price</span></th>
<th t-if="display_discount" t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
<span>Disc.(%)</span>
<t t-set="colspan" t-value="colspan+1"/>
</th>
<th t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"><span>Taxes</span></th>
<th class="text-right">
<span groups="account.group_show_line_subtotals_tax_excluded">Amount</span>
<span groups="account.group_show_line_subtotals_tax_included">Total Price</span>
</th>
<th class="text-right" groups="account.group_show_line_subtotals_tax_excluded">Amount</th>
<th class="text-right" groups="account.group_show_line_subtotals_tax_included">Total Price</th>
</tr>
</thead>
<tbody class="invoice_tbody">
<t t-set="current_subtotal" t-value="0"/>
<t t-set="current_subtotal_tax_excluded" t-value="0"/>
<t t-set="current_subtotal_tax_included" t-value="0"/>
<t t-foreach="o.invoice_line_ids" t-as="line">
<t t-set="current_subtotal" t-value="current_subtotal + line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
<t t-set="current_subtotal" t-value="current_subtotal + line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
<t t-set="current_subtotal_tax_excluded" t-value="current_subtotal_tax_excluded + line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
<t t-set="current_subtotal_tax_included" t-value="current_subtotal_tax_included + line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
<tr t-att-class="'bg-200 font-weight-bold o_line_section' if line.display_type == 'line_section' else 'font-italic o_line_note' if line.display_type == 'line_note' else ''">
<t t-if="not line.display_type" name="account_invoice_line_accountable">
@@ -92,20 +89,23 @@
<td t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
<span t-esc="', '.join(map(lambda x: (x.description or x.name), line.invoice_line_tax_ids))"/>
</td>
<td class="text-right o_price_total">
<span t-field="line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
<span t-field="line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
<td class="text-right o_price_total" groups="account.group_show_line_subtotals_tax_excluded">
<span t-field="line.price_subtotal"/>
</td>
<td class="text-right o_price_total" groups="account.group_show_line_subtotals_tax_included">
<span t-field="line.price_total"/>
</td>
</t>
<t t-if="line.display_type == 'line_section'">
<td t-att-colspan="colspan">
<td colspan="99">
<span t-field="line.name"/>
</td>
<t t-set="current_section" t-value="line"/>
<t t-set="current_subtotal" t-value="0"/>
<t t-set="current_subtotal_tax_excluded" t-value="0"/>
<t t-set="current_subtotal_tax_included" t-value="0"/>
</t>
<t t-if="line.display_type == 'line_note'">
<td t-att-colspan="colspan">
<td colspan="99">
<span t-field="line.name"/>
</td>
</t>
@@ -113,12 +113,17 @@
<t t-if="current_section and (line_last or o.invoice_line_ids[line_index+1].display_type == 'line_section')">
<tr class="is-subtotal text-right">
<td t-att-colspan="colspan">
<td colspan="99">
<strong class="mr16">Subtotal</strong>
<span
t-esc="current_subtotal"
groups="account.group_show_line_subtotals_tax_excluded"
t-esc="current_subtotal_tax_excluded"
t-options='{"widget": "monetary", "display_currency": o.currency_id}'
/>
<span groups="account.group_show_line_subtotals_tax_included"> | <span
t-esc="current_subtotal_tax_included"
t-options='{"widget": "monetary", "display_currency": o.currency_id}'
/></span>
</td>
</tr>
</t>
@@ -28,7 +28,7 @@ class AccountRegisterPayments(models.TransientModel):
def _onchange_amount(self):
if hasattr(super(AccountRegisterPayments, self), '_onchange_amount'):
super(AccountRegisterPayments, self)._onchange_amount()
self.check_amount_in_words = self.currency_id.amount_to_text(self.amount)
self.check_amount_in_words = self.currency_id.amount_to_text(self.amount) if self.currency_id else False
def _prepare_payment_vals(self, invoices):
res = super(AccountRegisterPayments, self)._prepare_payment_vals(invoices)
+8 -2
View File
@@ -131,7 +131,7 @@ class ImDispatch(object):
event = self.Event()
for channel in channels:
self.channels.setdefault(hashable(channel), []).append(event)
self.channels.setdefault(hashable(channel), set()).add(event)
try:
event.wait(timeout=timeout)
with registry.cursor() as cr:
@@ -140,6 +140,12 @@ class ImDispatch(object):
except Exception:
# timeout
pass
finally:
# gc pointers to event
for channel in channels:
channel_events = self.channels.get(hashable(channel))
if channel_events and event in channel_events:
channel_events.remove(event)
return notifications
def loop(self):
@@ -160,7 +166,7 @@ class ImDispatch(object):
# dispatch to local threads/greenlets
events = set()
for channel in channels:
events.update(self.channels.pop(hashable(channel), []))
events.update(self.channels.pop(hashable(channel), set()))
for event in events:
event.set()
+3
View File
@@ -93,6 +93,9 @@ class Employee(models.Model):
show_leaves = fields.Boolean('Able to see Remaining Time Off', compute='_compute_show_leaves')
is_absent = fields.Boolean('Absent Today', compute='_compute_leave_status', search='_search_absent_employee')
def _get_date_start_work(self):
return self.create_date
def _get_remaining_leaves(self):
""" Helper to compute the remaining leaves for the current employees
:returns dict where the key is the employee id, and the value is the remain leaves
@@ -167,16 +167,15 @@ class HolidaysAllocation(models.Model):
# We have to check when the employee has been created
# in order to not allocate him/her too much leaves
creation_date = fields.Datetime.from_string(holiday.employee_id.create_date)
start_date = holiday.employee_id._get_date_start_work()
# If employee is created after the period, we cancel the computation
if period_end <= creation_date:
if period_end <= start_date:
holiday.write(values)
continue
# If employee created during the period, taking the date at which he has been created
if period_start <= creation_date:
period_start = creation_date
if period_start <= start_date:
period_start = start_date
worked = holiday.employee_id._get_work_days_data(period_start, period_end, domain=[('holiday_id.holiday_status_id.unpaid', '=', True), ('time_type', '=', 'leave')])['days']
left = holiday.employee_id._get_leave_days_data(period_start, period_end, domain=[('holiday_id.holiday_status_id.unpaid', '=', True), ('time_type', '=', 'leave')])['days']
+7 -3
View File
@@ -2,6 +2,9 @@
""" ESC/POS Commands (Constants) """
# Control characters
ESC = '\x1b'
# Feed control sequences
CTL_LF = '\x0a' # Print and line feed
CTL_FF = '\x0c' # Form feed
@@ -19,9 +22,10 @@ DLE_EOT_PAPER = '\x10\x04\x04'
HW_INIT = '\x1b\x40' # Clear data in buffer and reset modes
HW_SELECT = '\x1b\x3d\x01' # Printer select
HW_RESET = '\x1b\x3f\x0a\x00' # Reset printer hardware
# Cash Drawer
CD_KICK_2 = '\x1b\x70\x00' # Sends a pulse to pin 2 []
CD_KICK_5 = '\x1b\x70\x01' # Sends a pulse to pin 5 []
# Cash Drawer (ESC p <pin> <on time: 2*ms> <off time: 2*ms>)
_CASH_DRAWER = lambda m, t1='', t2='': ESC + 'p' + m + chr(t1) + chr(t2)
CD_KICK_2 = _CASH_DRAWER('\x00', 50, 50) # Sends a pulse to pin 2 []
CD_KICK_5 = _CASH_DRAWER('\x01', 50, 50) # Sends a pulse to pin 5 []
# Paper
PAPER_FULL_CUT = '\x1d\x56\x00' # Full cut paper
PAPER_PART_CUT = '\x1d\x56\x01' # Partial cut paper
@@ -30,7 +30,6 @@ class MailChannel(models.Model):
_name = 'mail.channel'
_inherit = ['mail.channel', 'rating.mixin']
_description = 'Discussion channel'
anonymous_name = fields.Char('Anonymous Name')
channel_type = fields.Selection(selection_add=[('livechat', 'Livechat Conversation')])
+1 -1
View File
@@ -1024,7 +1024,7 @@
<field name="account_id" ref="pcg_445203"/>
<field name="sequence" eval="10"/>
<field name="type_tax_use">purchase</field>
<field name="tag_ids" eval="[(6,0,[ref('l10n_fr.tag_fr_73'),ref('l10n_fr.tag_fr_82')])]"/>
<field name="tag_ids" eval="[(6,0,[ref('l10n_fr.tag_fr_73'),ref('l10n_fr.tag_fr_64')])]"/>
<field name="tax_group_id" ref="tax_group_intra_21"/>
</record>
@@ -324,6 +324,9 @@
"8043","Omzet buiten EU werk derden","8043","account.data_account_type_revenue","l10n_nl.l10nnl_chart_template","l10n_nl.account_tag_11","False"
"8044","Omzet Intercompany werk derden","8044","account.data_account_type_revenue","l10n_nl.l10nnl_chart_template","l10n_nl.account_tag_11","False"
"8090","Diverse overige opbrengsten","8090","account.data_account_type_revenue","l10n_nl.l10nnl_chart_template","l10n_nl.account_tag_11","False"
"8110","Installatie/afstandsverkopen binnen EU handelsgoederen 1","80011","account.data_account_type_revenue","l10n_nl.l10nnl_chart_template","l10n_nl.account_tag_11","False"
"8120","Installatie/afstandsverkopen binnen EU handelsgoederen 2","80012","account.data_account_type_revenue","l10n_nl.l10nnl_chart_template","l10n_nl.account_tag_11","False"
"8130","Installatie/afstandsverkopen binnen EU handelsgoederen 3","80013","account.data_account_type_revenue","l10n_nl.l10nnl_chart_template","l10n_nl.account_tag_11","False"
"8920","Koersverschillen","8920","account.data_account_type_revenue","l10n_nl.l10nnl_chart_template","l10n_nl.account_tag_31","False"
"9011","Resultaat deelneming 1","9011","account.data_account_type_other_income","l10n_nl.l10nnl_chart_template","l10n_nl.account_tag_12","False"
"9012","Resultaat deelneming 2","9012","account.data_account_type_other_income","l10n_nl.l10nnl_chart_template","l10n_nl.account_tag_12","False"
1 id name code user_type_id/id chart_template_id/id tag_ids/id reconcile
324 8043 Omzet buiten EU werk derden 8043 account.data_account_type_revenue l10n_nl.l10nnl_chart_template l10n_nl.account_tag_11 False
325 8044 Omzet Intercompany werk derden 8044 account.data_account_type_revenue l10n_nl.l10nnl_chart_template l10n_nl.account_tag_11 False
326 8090 Diverse overige opbrengsten 8090 account.data_account_type_revenue l10n_nl.l10nnl_chart_template l10n_nl.account_tag_11 False
327 8110 Installatie/afstandsverkopen binnen EU handelsgoederen 1 80011 account.data_account_type_revenue l10n_nl.l10nnl_chart_template l10n_nl.account_tag_11 False
328 8120 Installatie/afstandsverkopen binnen EU handelsgoederen 2 80012 account.data_account_type_revenue l10n_nl.l10nnl_chart_template l10n_nl.account_tag_11 False
329 8130 Installatie/afstandsverkopen binnen EU handelsgoederen 3 80013 account.data_account_type_revenue l10n_nl.l10nnl_chart_template l10n_nl.account_tag_11 False
330 8920 Koersverschillen 8920 account.data_account_type_revenue l10n_nl.l10nnl_chart_template l10n_nl.account_tag_31 False
331 9011 Resultaat deelneming 1 9011 account.data_account_type_other_income l10n_nl.l10nnl_chart_template l10n_nl.account_tag_12 False
332 9012 Resultaat deelneming 2 9012 account.data_account_type_other_income l10n_nl.l10nnl_chart_template l10n_nl.account_tag_12 False
@@ -92,5 +92,30 @@
<field name="account_dest_id" ref="8022" />
</record>
<!-- Installatie / afstandsverkopen -->
<record
id="fiscal_position_account_installatie_afstand_1"
model="account.fiscal.position.account.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report" />
<field name="account_src_id" ref="8001" />
<field name="account_dest_id" ref="8110" />
</record>
<record
id="fiscal_position_account_installatie_afstand_2"
model="account.fiscal.position.account.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report" />
<field name="account_src_id" ref="8011" />
<field name="account_dest_id" ref="8120" />
</record>
<record
id="fiscal_position_account_installatie_afstand_3"
model="account.fiscal.position.account.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report" />
<field name="account_src_id" ref="8021" />
<field name="account_dest_id" ref="8130" />
</record>
</data>
</openerp>
</openerp>
@@ -34,7 +34,7 @@
<record id="position_tax_extracom_d_0" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_non_eu"/>
<field name="tax_src_id" ref="btw_0_d"/>
<field name="tax_dest_id" ref="btw_X3"/>
<field name="tax_dest_id" ref="btw_X1"/>
</record>
<record id="position_tax_extracom_d_6" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_non_eu"/>
@@ -49,12 +49,12 @@
<record id="position_tax_extracom_d_21" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_non_eu"/>
<field name="tax_src_id" ref="btw_21_d"/>
<field name="tax_dest_id" ref="btw_X3"/>
<field name="tax_dest_id" ref="btw_X1"/>
</record>
<record id="position_tax_extracom_d_overig" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_non_eu"/>
<field name="tax_src_id" ref="btw_overig_d"/>
<field name="tax_dest_id" ref="btw_X3"/>
<field name="tax_dest_id" ref="btw_X1"/>
</record>
<!-- VAT on buying from outside the EU -->
<record id="position_tax_extracom_6" model="account.fiscal.position.tax.template">
@@ -65,7 +65,7 @@
<record id="position_tax_extracom_9" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_non_eu"/>
<field name="tax_src_id" ref="btw_9_buy"/>
<field name="tax_dest_id" ref="btw_E1"/>
<field name="tax_dest_id" ref="btw_E1_9"/>
</record>
<record id="position_tax_extracom_7" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_non_eu"/>
@@ -85,7 +85,7 @@
<record id="position_tax_extracom_d_9" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_non_eu"/>
<field name="tax_src_id" ref="btw_9_buy_d"/>
<field name="tax_dest_id" ref="btw_E1"/>
<field name="tax_dest_id" ref="btw_E1_9"/>
</record>
<record id="position_tax_extracom_d_7" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_non_eu"/>
@@ -95,59 +95,59 @@
<record id="position_tax_extracom_d_8" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_non_eu"/>
<field name="tax_src_id" ref="btw_overig_buy_d"/>
<field name="tax_dest_id" ref="btw_E_overig"/>
<field name="tax_dest_id" ref="btw_E_overig_d"/>
</record>
<!-- EU Countries -->
<record id="position_tax_intracom_1" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu"/>
<field name="tax_src_id" ref="btw_0"/>
<field name="tax_dest_id" ref="btw_X0_diensten"/>
<field name="tax_dest_id" ref="btw_X0_producten"/>
</record>
<record id="position_tax_intracom_2" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu"/>
<field name="tax_src_id" ref="btw_6"/>
<field name="tax_dest_id" ref="btw_X0_diensten"/>
<field name="tax_dest_id" ref="btw_X0_producten"/>
</record>
<record id="position_tax_intracom_2_9" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu"/>
<field name="tax_src_id" ref="btw_9"/>
<field name="tax_dest_id" ref="btw_X0_diensten"/>
<field name="tax_dest_id" ref="btw_X0_producten"/>
</record>
<record id="position_tax_intracom_3" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu"/>
<field name="tax_src_id" ref="btw_21"/>
<field name="tax_dest_id" ref="btw_X0_diensten"/>
<field name="tax_dest_id" ref="btw_X0_producten"/>
</record>
<record id="position_tax_intracom_4" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu"/>
<field name="tax_src_id" ref="btw_overig"/>
<field name="tax_dest_id" ref="btw_X0_diensten"/>
<field name="tax_dest_id" ref="btw_X0_producten"/>
</record>
<record id="position_tax_intracom_d_1" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu"/>
<field name="tax_src_id" ref="btw_0_d"/>
<field name="tax_dest_id" ref="btw_X2"/>
<field name="tax_dest_id" ref="btw_X0_diensten"/>
</record>
<record id="position_tax_intracom_d_2" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu"/>
<field name="tax_src_id" ref="btw_6_d"/>
<field name="tax_dest_id" ref="btw_X2"/>
<field name="tax_dest_id" ref="btw_X0_diensten"/>
</record>
<record id="position_tax_intracom_d_2_9" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu"/>
<field name="tax_src_id" ref="btw_9_d"/>
<field name="tax_dest_id" ref="btw_X2"/>
<field name="tax_dest_id" ref="btw_X0_diensten"/>
</record>
<record id="position_tax_intracom_d_3" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu"/>
<field name="tax_src_id" ref="btw_21_d"/>
<field name="tax_dest_id" ref="btw_X2"/>
<field name="tax_dest_id" ref="btw_X0_diensten"/>
</record>
<record id="position_tax_intracom_d_4" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu"/>
<field name="tax_src_id" ref="btw_overig_d"/>
<field name="tax_dest_id" ref="btw_X2"/>
<field name="tax_dest_id" ref="btw_X0_diensten"/>
</record>
<record id="position_tax_intracom_6" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu"/>
@@ -172,22 +172,22 @@
<record id="position_tax_intracom_d_6" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu"/>
<field name="tax_src_id" ref="btw_6_buy_d"/>
<field name="tax_dest_id" ref="btw_I_6"/>
<field name="tax_dest_id" ref="btw_I_6_d"/>
</record>
<record id="position_tax_intracom_d_9" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu"/>
<field name="tax_src_id" ref="btw_9_buy_d"/>
<field name="tax_dest_id" ref="btw_I_9"/>
<field name="tax_dest_id" ref="btw_I_9_d"/>
</record>
<record id="position_tax_intracom_d_7" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu"/>
<field name="tax_src_id" ref="btw_21_buy_d"/>
<field name="tax_dest_id" ref="btw_I_21"/>
<field name="tax_dest_id" ref="btw_I_21_d"/>
</record>
<record id="position_tax_intracom_d_8" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu"/>
<field name="tax_src_id" ref="btw_overig_buy_d"/>
<field name="tax_dest_id" ref="btw_I_overig"/>
<field name="tax_dest_id" ref="btw_I_overig_d"/>
</record>
<!-- BTW verlegd -->
@@ -196,6 +196,5 @@
<field name="tax_src_id" ref="btw_21_buy"/>
<field name="tax_dest_id" ref="btw_ink_0"/>
</record>
</data>
</openerp>
@@ -28,83 +28,57 @@
<record id="position_tax_eu_no_taxes_report_1" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_0"/>
<field name="tax_dest_id" ref="btw_X2"/>
</record>
<record id="position_tax_eu_no_taxes_report_2" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_6"/>
<field name="tax_src_id" ref="btw_verk_0"/>
<field name="tax_dest_id" ref="btw_X2"/>
</record>
<record id="position_tax_eu_no_taxes_report_3" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_21"/>
<field name="tax_src_id" ref="btw_6"/>
<field name="tax_dest_id" ref="btw_X2"/>
</record>
<record id="position_tax_eu_no_taxes_report_4" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_overig"/>
<field name="tax_src_id" ref="btw_9"/>
<field name="tax_dest_id" ref="btw_X2"/>
</record>
<record id="position_tax_eu_no_taxes_report_5" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_0_d"/>
<field name="tax_src_id" ref="btw_21"/>
<field name="tax_dest_id" ref="btw_X2"/>
</record>
<record id="position_tax_eu_no_taxes_report_6" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_6_d"/>
<field name="tax_src_id" ref="btw_overig"/>
<field name="tax_dest_id" ref="btw_X2"/>
</record>
<record id="position_tax_eu_no_taxes_report_7" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_21_d"/>
<field name="tax_src_id" ref="btw_0_d"/>
<field name="tax_dest_id" ref="btw_X2"/>
</record>
<record id="position_tax_eu_no_taxes_report_8" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_overig_d"/>
<field name="tax_src_id" ref="btw_6_d"/>
<field name="tax_dest_id" ref="btw_X2"/>
</record>
<record id="position_tax_eu_no_taxes_report_9" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_6_buy"/>
<field name="tax_src_id" ref="btw_9_d"/>
<field name="tax_dest_id" ref="btw_X2"/>
</record>
<record id="position_tax_eu_no_taxes_report_10" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_6_buy_incl"/>
<field name="tax_src_id" ref="btw_21_d"/>
<field name="tax_dest_id" ref="btw_X2"/>
</record>
<record id="position_tax_eu_no_taxes_report_11" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_21_buy"/>
<field name="tax_src_id" ref="btw_overig_d"/>
<field name="tax_dest_id" ref="btw_X2"/>
</record>
<record id="position_tax_eu_no_taxes_report_12" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_21_buy_incl"/>
</record>
<record id="position_tax_eu_no_taxes_report_13" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_overig_buy"/>
</record>
<record id="position_tax_eu_no_taxes_report_14" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_6_buy_d"/>
</record>
<record id="position_tax_eu_no_taxes_report_15" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_21_buy_d"/>
</record>
<record id="position_tax_eu_no_taxes_report_16" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_overig_buy_d"/>
</record>
<record id="position_tax_eu_no_taxes_report_17" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_verk_0"/>
</record>
<record id="position_tax_eu_no_taxes_report_18" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_ink_0_1"/>
</record>
<record id="position_tax_eu_no_taxes_report_19" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_ink_0_2"/>
</record>
<record id="position_tax_eu_no_taxes_report_20" model="account.fiscal.position.tax.template">
<field name="position_id" ref="fiscal_position_template_eu_no_taxes_report"/>
<field name="tax_src_id" ref="btw_ink_0"/>
</record>
</data>
</openerp>
+1 -1
View File
@@ -200,7 +200,7 @@ var DocumentViewer = Widget.extend({
*/
_onDownload: function (e) {
e.preventDefault();
window.location = '/web/content/' + this.activeAttachment.id + '?download=true';
window.location = '/web/content/' + this.modelName + '/' + this.activeAttachment.id + '/' + 'datas' + '?download=true';
},
/**
* @private
+2 -2
View File
@@ -318,9 +318,9 @@ class MassMailingCampaign(models.Model):
campaign_id = fields.Many2one('utm.campaign', 'campaign_id',
required=True, ondelete='cascade', help="This name helps you tracking your different campaign efforts, e.g. Fall_Drive, Christmas_Special")
source_id = fields.Many2one('utm.source', string='Source',
help="This is the link source, e.g. Search Engine, another domain,or name of email list", default=lambda self: self.env.ref('utm.utm_source_newsletter'))
help="This is the link source, e.g. Search Engine, another domain,or name of email list", default=lambda self: self.env.ref('utm.utm_source_newsletter', False))
medium_id = fields.Many2one('utm.medium', string='Medium',
help="This is the delivery method, e.g. Postcard, Email, or Banner Ad", default=lambda self: self.env.ref('utm.utm_medium_email'))
help="This is the delivery method, e.g. Postcard, Email, or Banner Ad", default=lambda self: self.env.ref('utm.utm_medium_email', False))
tag_ids = fields.Many2many(
'mail.mass_mailing.tag', 'mail_mass_mailing_tag_rel',
'tag_id', 'campaign_id', string='Tags')
@@ -205,10 +205,10 @@ class ReportBomStructure(models.AbstractModel):
def _get_pdf_line(self, bom_id, product_id=False, qty=1, child_bom_ids=[], unfolded=False):
data = self._get_bom(bom_id=bom_id, product_id=product_id, line_qty=qty)
data = self._get_bom(bom_id=bom_id, product_id=product_id.id, line_qty=qty)
def get_sub_lines(bom, product_id, line_qty, line_id, level):
data = self._get_bom(bom_id=bom.id, product_id=product_id, line_qty=line_qty, line_id=line_id, level=level)
data = self._get_bom(bom_id=bom.id, product_id=product_id.id, line_qty=line_qty, line_id=line_id, level=level)
bom_lines = data['components']
lines = []
for bom_line in bom_lines:
+5
View File
@@ -71,6 +71,11 @@ class AccountPayment(models.Model):
return transactions
def action_validate_invoice_payment(self):
res = super(AccountPayment, self).action_validate_invoice_payment()
self.mapped('payment_transaction_id').filtered(lambda x: x.state == 'done' and not x.is_processed)._post_process_after_done()
return res
@api.multi
def post(self):
# Post the payments "normally" if no transactions are needed.
@@ -32,12 +32,12 @@ class BuckarooForm(BuckarooCommon):
# ----------------------------------------
form_values = {
'add_returndata': '',
'add_returndata': '/payment/process',
'Brq_websitekey': self.buckaroo.brq_websitekey,
'Brq_amount': '2240.0',
'Brq_currency': 'EUR',
'Brq_invoicenumber': 'SO004',
'Brq_signature': 'aa6fe072afdd9d1b463d55f43e6df4b272d3529a', # update me
'Brq_signature': 'f09bb79451a7ddef3178462ea52b07e55d34e99e', # update me
'brq_test': 'True',
'Brq_return': urls.url_join(base_url, BuckarooController._return_url),
'Brq_returncancel': urls.url_join(base_url, BuckarooController._cancel_url),
+2 -1
View File
@@ -67,6 +67,7 @@ class PaypalForm(PaypalCommon):
'return': urls.url_join(base_url, PaypalController._return_url),
'notify_url': urls.url_join(base_url, PaypalController._notify_url),
'cancel_return': urls.url_join(base_url, PaypalController._cancel_url),
'custom': '{"return_url": "/payment/process"}',
}
# check form result
@@ -140,7 +141,7 @@ class PaypalForm(PaypalCommon):
'item_name': u'test_ref_2',
'address_country': u'France',
'charset': u'windows-1252',
'custom': u'',
'custom': u'{"return_url": "/payment/process"}',
'notify_version': u'3.7',
'address_name': u'Norbert Poilu',
'pending_reason': u'multi_currency',
@@ -331,6 +331,12 @@ class ProductTemplate(models.Model):
raise ValidationError(_('The default Unit of Measure and the purchase Unit of Measure must be in the same category.'))
return True
@api.constrains('attribute_line_ids')
def _check_attribute_line(self):
if any(len(template.attribute_line_ids) != len(template.attribute_line_ids.mapped('attribute_id')) for template in self):
raise ValidationError(_('You cannot define two attribute lines for the same attribute.'))
return True
@api.onchange('uom_id')
def _onchange_uom_id(self):
if self.uom_id:
-10
View File
@@ -2,16 +2,6 @@
<odoo>
<data noupdate="1">
<!-- sale_timesheet and project define the same field without depending on each, which causes the field to be deleted when the module that created it
is deleted. To avoid this, we create xmlids manually for this field in both modules to prevent accidental deletion. To fix in saas-7 by moving the field definition-->
<record id="duplicate_field_xmlid" model="ir.model.data">
<field name="res_id" search="[('model','=','res.company'),('name','=','project_time_mode_id')]" model="ir.model.fields"/>
<field name="model">ir.model.fields</field>
<field name="module">project</field>
<field name="name">project_time_mode_id_duplicate_xmlid</field>
<field name="noupdate">True</field>
</record>
<record id="ir_cron_rating_project" model="ir.cron">
<field name="name">Project: Send rating</field>
<field name="model_id" ref="project.model_project_project"/>
+6 -1
View File
@@ -131,7 +131,7 @@ class AccountInvoice(models.Model):
res = super(AccountInvoice, self)._onchange_partner_id()
if payment_term_id:
self.payment_term_id = payment_term_id
if not self.env.context.get('default_journal_id') and self.partner_id and self.currency_id and\
if not self.env.context.get('default_journal_id') and self.partner_id and\
self.type in ['in_invoice', 'in_refund'] and\
self.currency_id != self.partner_id.property_purchase_currency_id and\
self.partner_id.property_purchase_currency_id.id:
@@ -172,6 +172,11 @@ class AccountInvoice(models.Model):
invoice.message_post(body=message)
return result
def _get_onchange_create(self):
res = super()._get_onchange_create()
res['_onchange_partner_id'].append('currency_id')
return res
class AccountInvoiceLine(models.Model):
""" Override AccountInvoice_line to add the link to the purchase order line it is related to"""
@@ -1,22 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_purchaseorder_document" inherit_id="purchase.report_purchaseorder_document">
<xpath expr="//div[@name='purchase_shipping_address']" position="after">
<div t-if="not o.dest_address_id and o.picking_type_id and o.picking_type_id.warehouse_id">
<span t-field="o.picking_type_id.warehouse_id.name"/>
<div t-field="o.picking_type_id.warehouse_id.partner_id" t-options='{"widget": "contact", "fields": ["address", "phone"], "no_marker": True, "phone_icons": True}'/>
</div>
</xpath>
</template>
<template id="report_purchaseorder_document" inherit_id="purchase.report_purchaseorder_document">
<xpath expr="//t[@t-if='o.dest_address_id']" position="after">
<t t-else="">
<t t-set="information_block">
<strong>Shipping address:</strong>
<div t-if="o.picking_type_id and o.picking_type_id.warehouse_id">
<span t-field="o.picking_type_id.warehouse_id.name"/>
<div t-field="o.picking_type_id.warehouse_id.partner_id" t-options='{"widget": "contact", "fields": ["address", "phone"], "no_marker": True, "phone_icons": True}'/>
</div>
</t>
</t>
</xpath>
</template>
<template id="report_purchasequotation_document" inherit_id="purchase.report_purchasequotation_document">
<xpath expr="//div[@name='purchase_shipping_address']" position="after">
<div t-if="not o.dest_address_id and o.picking_type_id.warehouse_id">
<span t-field="o.picking_type_id.warehouse_id.name"/>
<div t-field="o.picking_type_id.warehouse_id.partner_id" t-options='{"widget": "contact", "fields": ["address", "phone"], "no_marker": True, "phone_icons": True}'/>
</div>
</xpath>
</template>
<template id="report_purchasequotation_document" inherit_id="purchase.report_purchasequotation_document">
<xpath expr="//t[@t-if='o.dest_address_id']" position="after">
<t t-else="">
<t t-set="information_block">
<strong>Shipping address:</strong>
<div t-if="o.picking_type_id and o.picking_type_id.warehouse_id">
<span t-field="o.picking_type_id.warehouse_id.name"/>
<div t-field="o.picking_type_id.warehouse_id.partner_id" t-options='{"widget": "contact", "fields": ["address", "phone"], "no_marker": True, "phone_icons": True}'/>
</div>
</t>
</t>
</xpath>
</template>
</odoo>
+27 -13
View File
@@ -599,17 +599,24 @@ class RepairLine(models.Model):
'title': _('No pricelist found.'),
'message':
_('You have to select a pricelist in the Repair form !\n Please set one before choosing a product.')}
else:
price = pricelist.get_product_price(self.product_id, self.product_uom_qty, partner)
if price is False:
warning = {
'title': _('No valid pricelist line found.'),
'message':
_("Couldn't find a pricelist line matching this product and quantity.\nYou have to change either the product, the quantity or the pricelist.")}
else:
self.price_unit = price
if warning:
return {'warning': warning}
else:
self._onchange_product_uom()
@api.onchange('product_uom')
def _onchange_product_uom(self):
partner = self.repair_id.partner_id
pricelist = self.repair_id.pricelist_id
if pricelist and self.product_id and self.type != 'remove':
price = pricelist.get_product_price(self.product_id, self.product_uom_qty, partner, uom_id=self.product_uom.id)
if price is False:
warning = {
'title': _('No valid pricelist line found.'),
'message':
_("Couldn't find a pricelist line matching this product and quantity.\nYou have to change either the product, the quantity or the pricelist.")}
return {'warning': warning}
else:
self.price_unit = price
class RepairFee(models.Model):
@@ -659,14 +666,21 @@ class RepairFee(models.Model):
'title': _('No pricelist found.'),
'message':
_('You have to select a pricelist in the Repair form !\n Please set one before choosing a product.')}
return {'warning': warning}
else:
price = pricelist.get_product_price(self.product_id, self.product_uom_qty, partner)
self._onchange_product_uom()
@api.onchange('product_uom')
def _onchange_product_uom(self):
partner = self.repair_id.partner_id
pricelist = self.repair_id.pricelist_id
if pricelist and self.product_id:
price = pricelist.get_product_price(self.product_id, self.product_uom_qty, partner, uom_id=self.product_uom.id)
if price is False:
warning = {
'title': _('No valid pricelist line found.'),
'message':
_("Couldn't find a pricelist line matching this product and quantity.\nYou have to change either the product, the quantity or the pricelist.")}
return {'warning': warning}
else:
self.price_unit = price
if warning:
return {'warning': warning}
+22 -17
View File
@@ -70,29 +70,26 @@
<table class="table table-sm o_main_table">
<thead>
<tr>
<t t-set="colspan" t-value="5"/>
<th class="text-left">Description</th>
<th class="text-right">Quantity</th>
<th class="text-right">Unit Price</th>
<th t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">
<span>Disc.(%)</span>
<t t-set="colspan" t-value="colspan+1"/>
</th>
<th class="text-right">Taxes</th>
<th class="text-right">
<t groups="account.group_show_line_subtotals_tax_excluded">Amount</t>
<t groups="account.group_show_line_subtotals_tax_included">Total Price</t>
</th>
<th class="text-right" groups="account.group_show_line_subtotals_tax_excluded">Amount</th>
<th class="text-right" groups="account.group_show_line_subtotals_tax_included">Total Price</th>
</tr>
</thead>
<tbody class="sale_tbody">
<t t-set="current_subtotal" t-value="0"/>
<t t-set="current_subtotal_tax_excluded" t-value="0"/>
<t t-set="current_subtotal_tax_included" t-value="0"/>
<t t-foreach="doc.order_line" t-as="line">
<t t-set="current_subtotal" t-value="current_subtotal + line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
<t t-set="current_subtotal" t-value="current_subtotal + line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
<t t-set="current_subtotal_tax_excluded" t-value="current_subtotal_tax_excluded + line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
<t t-set="current_subtotal_tax_included" t-value="current_subtotal_tax_included + line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
<tr t-att-class="'bg-200 font-weight-bold o_line_section' if line.display_type == 'line_section' else 'font-italic o_line_note' if line.display_type == 'line_note' else ''">
<t t-if="not line.display_type">
@@ -110,20 +107,23 @@
<td class="text-right">
<span t-esc="', '.join(map(lambda x: (x.description or x.name), line.tax_id))"/>
</td>
<td class="text-right o_price_total">
<span t-field="line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
<span t-field="line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
<td class="text-right o_price_total" groups="account.group_show_line_subtotals_tax_excluded">
<span t-field="line.price_subtotal"/>
</td>
<td class="text-right o_price_total" groups="account.group_show_line_subtotals_tax_included">
<span t-field="line.price_total"/>
</td>
</t>
<t t-if="line.display_type == 'line_section'">
<td t-att-colspan="99">
<td colspan="99">
<span t-field="line.name"/>
</td>
<t t-set="current_section" t-value="line"/>
<t t-set="current_subtotal" t-value="0"/>
<t t-set="current_subtotal_tax_excluded" t-value="0"/>
<t t-set="current_subtotal_tax_included" t-value="0"/>
</t>
<t t-if="line.display_type == 'line_note'">
<td t-att-colspan="99">
<td colspan="99">
<span t-field="line.name"/>
</td>
</t>
@@ -131,12 +131,17 @@
<t t-if="current_section and (line_last or doc.order_line[line_index+1].display_type == 'line_section')">
<tr class="is-subtotal text-right">
<td t-att-colspan="99">
<td colspan="99" groups="account.group_show_line_subtotals_tax_excluded,account.group_show_line_subtotals_tax_included">
<strong class="mr16">Subtotal</strong>
<span
t-esc="current_subtotal"
groups="account.group_show_line_subtotals_tax_excluded"
t-esc="current_subtotal_tax_excluded"
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
/>
<span groups="account.group_show_line_subtotals_tax_included"> | <span
t-esc="current_subtotal_tax_included"
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
/></span>
</td>
</tr>
</t>
+23 -18
View File
@@ -424,29 +424,26 @@
<table class="table table-sm" id="sales_order_table">
<thead class="bg-100">
<tr>
<t t-set="colspan" t-value="6"/>
<th class="text-left">Products</th>
<th class="text-right">Quantity</th>
<th t-attf-class="text-right {{ 'd-none d-sm-table-cell' if report_type == 'html' else '' }}">Unit Price</th>
<th t-if="display_discount" t-attf-class="text-right {{ 'd-none d-sm-table-cell' if report_type == 'html' else '' }}">
<span>Discount</span>
<t t-set="colspan" t-value="colspan+1"/>
</th>
<th t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">Taxes</th>
<th class="text-right" >
<span groups="account.group_show_line_subtotals_tax_excluded">Amount</span>
<span groups="account.group_show_line_subtotals_tax_included">Total Price</span>
</th>
<th class="text-right" groups="account.group_show_line_subtotals_tax_excluded">Amount</th>
<th class="text-right" groups="account.group_show_line_subtotals_tax_included">Total Price</th>
</tr>
</thead>
<tbody class="sale_tbody">
<t t-set="current_subtotal" t-value="0"/>
<t t-set="current_subtotal_tax_excluded" t-value="0"/>
<t t-set="current_subtotal_tax_included" t-value="0"/>
<t t-foreach="sale_order.order_line" t-as="line">
<t t-set="current_subtotal" t-value="current_subtotal + line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
<t t-set="current_subtotal" t-value="current_subtotal + line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
<t t-set="current_subtotal_tax_excluded" t-value="current_subtotal_tax_excluded + line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
<t t-set="current_subtotal_tax_included" t-value="current_subtotal_tax_included + line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
<tr t-att-class="'bg-200 font-weight-bold o_line_section' if line.display_type == 'line_section' else 'font-italic o_line_note' if line.display_type == 'line_note' else ''">
<t t-if="not line.display_type">
@@ -477,20 +474,23 @@
<td t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
<span t-esc="', '.join(map(lambda x: (x.description or x.name), line.tax_id))"/>
</td>
<td class="text-right">
<span t-field="line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
<span t-field="line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
<td class="text-right o_price_total" groups="account.group_show_line_subtotals_tax_excluded">
<span t-field="line.price_subtotal"/>
</td>
<td class="text-right o_price_total" groups="account.group_show_line_subtotals_tax_included">
<span t-field="line.price_total"/>
</td>
</t>
<t t-if="line.display_type == 'line_section'">
<td t-att-colspan="colspan">
<td colspan="99">
<span t-field="line.name"/>
</td>
<t t-set="current_section" t-value="line"/>
<t t-set="current_subtotal" t-value="0"/>
<t t-set="current_subtotal_tax_excluded" t-value="0"/>
<t t-set="current_subtotal_tax_included" t-value="0"/>
</t>
<t t-if="line.display_type == 'line_note'">
<td t-att-colspan="colspan">
<td colspan="99">
<span t-field="line.name"/>
</td>
</t>
@@ -498,12 +498,17 @@
<t t-if="current_section and (line_last or sale_order.order_line[line_index+1].display_type == 'line_section')">
<tr class="is-subtotal text-right">
<td t-att-colspan="colspan">
<td colspan="99">
<strong class="mr16">Subtotal</strong>
<span
t-esc="current_subtotal"
t-options='{"widget": "monetary", "display_currency": sale_order.pricelist_id.currency_id}'
groups="account.group_show_line_subtotals_tax_excluded"
t-esc="current_subtotal_tax_excluded"
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
/>
<span groups="account.group_show_line_subtotals_tax_included"> | <span
t-esc="current_subtotal_tax_included"
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
/></span>
</td>
</tr>
</t>
@@ -34,3 +34,7 @@ class StockWarnInsufficientQtyScrap(models.TransientModel):
def action_done(self):
return self.scrap_id.do_scrap()
def action_cancel(self):
# FIXME in master: we should not have created the scrap in a first place
return self.scrap_id.sudo().unlink()
@@ -44,6 +44,9 @@
<xpath expr="//field[@name='product_id']" position="after">
<field name="scrap_id" invisible="1"/>
</xpath>
<xpath expr="//button[@name='cancel_button']" position="replace">
<button string="Discard" name="action_cancel" type="object" class="btn-primary"/>
</xpath>
</field>
</record>
</odoo>
@@ -91,20 +91,20 @@ var AbstractAction = Widget.extend(ActionMixin, {
*/
willStart: function () {
var self = this;
var defs = [this._super.apply(this, arguments)];
if (this.hasControlPanel) {
var params = this.controlPanelParams;
var def;
if (this.loadControlPanel) {
def = this
defs.push(this
.loadFieldView(params.modelName, params.context || {}, params.viewId, 'search')
.then(function (fieldsView) {
params.viewInfo = {
arch: fieldsView.arch,
fields: fieldsView.fields,
};
});
}));
}
return Promise.resolve(def).then(function () {
return Promise.all(defs).then(function () {
var controlPanelView = new self.config.ControlPanelView(params);
return controlPanelView.getController(self).then(function (controlPanel) {
self._controlPanel = controlPanel;
@@ -112,7 +112,7 @@ var AbstractAction = Widget.extend(ActionMixin, {
});
});
}
return Promise.resolve();
return Promise.all(defs);
},
/**
* @override
+5 -5
View File
@@ -231,13 +231,13 @@ var Dialog = Widget.extend({
this.$modal.remove();
}
if (!isFocusSet) {
var modals = $('body > .modal').filter(':visible');
if (modals.length) {
var modals = $('body > .modal').filter(':visible');
if (modals.length) {
if (!isFocusSet) {
modals.last().focus();
// Keep class modal-open (deleted by bootstrap hide fnct) on body to allow scrolling inside the modal
$('body').addClass('modal-open');
}
// Keep class modal-open (deleted by bootstrap hide fnct) on body to allow scrolling inside the modal
$('body').addClass('modal-open');
}
},
/**
@@ -75,9 +75,11 @@ return AbstractModel.extend({
.utc();
} else {
// default hours in the user's timezone
start.hours(7).add(-this.getSession().getTZOffset(start), 'minutes');
end.hours(19).add(-this.getSession().getTZOffset(end), 'minutes');
start.hours(7);
end.hours(19);
}
start.add(-this.getSession().getTZOffset(start), 'minutes');
end.add(-this.getSession().getTZOffset(end), 'minutes');
}
} else {
start.add(-this.getSession().getTZOffset(start), 'minutes');
@@ -685,6 +685,11 @@
.o_field_widget {
margin-bottom: $o-form-spacing-unit * 2;
> .o_field_widget {
margin-bottom: 0;
}
&.o_field_boolean {
margin-right: 0;
}
@@ -285,6 +285,68 @@ QUnit.module('relational_fields', {
form.destroy();
});
QUnit.test('focus when closing many2one modal in many2one modal', async function (assert) {
assert.expect(12);
var form = await createView({
View: FormView,
model: 'partner',
data: this.data,
arch: '<form string="Partners">' +
'<field name="trululu"/>' +
'</form>',
res_id: 2,
archs: {
'partner,false,form': '<form><field name="trululu"/></form>'
},
mockRPC: function (route, args) {
if (args.method === 'get_formview_id') {
return Promise.resolve(false);
}
return this._super(route, args);
},
});
// Open many2one modal
await testUtils.form.clickEdit(form);
await testUtils.dom.click(form.$('.o_external_button'));
var $originalModal = $('.modal-dialog');
var $focusedModal = $(document.activeElement).closest('.modal-dialog');
assert.equal($originalModal.length, 1, 'There should be one modal');
assert.equal($originalModal[0], $focusedModal[0], 'Modal is focused');
assert.ok($('body').hasClass('modal-open'), 'Modal is said opened');
// Open many2one modal of field in many2one modal
await testUtils.dom.click($originalModal.find('.o_external_button'));
var $modals = $('.modal-dialog');
$focusedModal = $(document.activeElement).closest('.modal-dialog');
assert.equal($modals.length, 2, 'There should be two modals');
assert.equal($modals[1], $focusedModal[0], 'Last modal is focused');
assert.ok($('body').hasClass('modal-open'), 'Modal is said opened');
// Close second modal
await testUtils.dom.click($modals.last().find('button[class="close"]'));
var $modal = $('.modal-dialog');
$focusedModal = $(document.activeElement).closest('.modal-dialog');
assert.equal($modal.length, 1, 'There should be one modal');
assert.equal($modal[0], $originalModal[0], 'First modal is still opened');
assert.equal($modal[0], $focusedModal[0], 'Modal is focused');
assert.ok($('body').hasClass('modal-open'), 'Modal is said opened');
// Close first modal
await testUtils.dom.click($modal.find('button[class="close"]'));
$modal = $('.modal-dialog.modal-lg');
assert.equal($modal.length, 0, 'There should be no modal');
assert.notOk($('body').hasClass('modal-open'), 'Modal is not said opened');
form.destroy();
});
QUnit.test('one2many from a model that has been sorted', async function (assert) {
assert.expect(1);
@@ -2267,6 +2267,61 @@ QUnit.module('Views', {
calendar.destroy();
});
QUnit.test('timezone does not affect drag and drop', async function (assert) {
assert.expect(6);
var calendar = await createCalendarView({
View: CalendarView,
model: 'event',
data: this.data,
arch:
'<calendar date_start="start" mode="month">'+
'<field name="name"/>'+
'<field name="start"/>'+
'</calendar>',
archs: archs,
viewOptions: {
initialDate: initialDate,
},
mockRPC: function (route, args) {
if (args.method === "write") {
assert.deepEqual(args.args[0], [6], "event 6 is moved")
assert.deepEqual(args.args[1].start, "2016-11-29 08:00:00",
"event moved to 27th nov 16h00 +40 hours timezone")
}
return this._super(route, args);
},
session: {
getTZOffset: function () {
return -2400; // 40 hours timezone
},
},
});
var events = calendar.$('.fc-event').map(function () {
return $(this).text().trim().replace(/\s+/g, '|');
});
assert.strictEqual(events[0], "event|1|12/09/2016|08:00:00");
assert.strictEqual(events[5], "event|6|12/16/2016|16:00:00");
// Move event 6 as on first day of month view (27th november 2016)
await testUtils.dragAndDrop(
calendar.$('.fc-event').eq(5),
calendar.$('.fc-day-top').first()
);
await testUtils.nextTick();
events = calendar.$('.fc-event').map(function () {
return $(this).text().trim().replace(/\s+/g, '|');
});
assert.strictEqual(events[0], "event|6|11/27/2016|16:00:00");
assert.strictEqual(events[1], "event|1|12/09/2016|08:00:00");
calendar.destroy();
});
QUnit.test('form_view_id attribute works (for creating events)', async function (assert) {
assert.expect(1);
@@ -224,7 +224,6 @@
.ActiveUsers {
position: relative;
float: right;
top: -60px;
right: 10px;
border: 1px solid gray('200');
font-weight: 300;
@@ -447,10 +447,10 @@
<div class="card-body">
<ul class="nav nav-pills flex-column">
<t t-foreach="nav_list" t-as="year">
<li data-toggle="collapse" t-att-data-target="'#' + year" aria-expanded="false" class="nav-item blog_post_year_collapse mt8">
<li data-toggle="collapse" t-att-data-target="'#b_' + year" aria-expanded="false" class="nav-item blog_post_year_collapse mt8">
<i t-attf-class="fa #{'fa-chevron-down' if not date and year_index == 0 else 'fa-chevron-right'} mr4" role="img" aria-label="Next year" title="Next year"/><span t-esc="year"/>
</li>
<ul t-attf-class="blog_post_year collapse nav nav-pills flex-column #{not date and year_index == 0 and 'in'}" t-att-id="year" role="menu">
<ul t-attf-class="blog_post_year collapse nav nav-pills flex-column #{not date and year_index == 0 and 'in'}" t-att-id="'b_' + year" role="menu">
<t t-foreach="nav_list[year]" t-as="months">
<li t-if="months['date_begin'] == date" class="nav-item">
<a role="menuitem" t-ignore="True" t-attf-href="#{blog_url(date_begin=False, date_end=False)}" class="nav-link active"><t t-esc="months['month']"/><span class="float-right badge badge-pill" t-esc="months['post_date_count']"/></a>
+1 -1
View File
@@ -36,7 +36,7 @@
</record>
<record model="product.pricelist" id="product.list0">
<field name="selectable" eval="False" />
<field name="selectable" eval="True" />
<field name="website_id" eval="False"/>
</record>
</data>
+1 -1
View File
@@ -91,7 +91,7 @@ class ProductPricelist(models.Model):
Change in this method should be reflected in `_get_website_pricelists_domain`.
"""
self.ensure_one()
return self.website_id.id == website_id or (not self.website_id and (self.selectable or self.code))
return self.website_id.id == website_id or (not self.website_id and (self.selectable or self.sudo().code))
def _get_website_pricelists_domain(self, website_id):
''' Check above `_is_available_on_website` for explanation.
+2 -1
View File
@@ -287,7 +287,8 @@ class SaleOrder(models.Model):
@api.multi
def action_recovery_email_send(self):
self._portal_ensure_token()
for order in self:
order._portal_ensure_token()
composer_form_view_id = self.env.ref('mail.email_compose_message_wizard_form').id
try:
default_template = self.env.ref('website_sale.mail_template_sale_cart_recovery', raise_if_not_found=False)
+6 -2
View File
@@ -720,12 +720,16 @@ class IrActionsReport(models.Model):
data = self._get_rendering_context(docids, data)
return self.render_template(self.report_name, data), 'html'
@api.model
def _get_rendering_context_model(self):
report_model_name = 'report.%s' % self.report_name
return self.env.get(report_model_name)
@api.model
def _get_rendering_context(self, docids, data):
# If the report is using a custom model to render its html, we must use it.
# Otherwise, fallback on the generic html rendering.
report_model_name = 'report.%s' % self.report_name
report_model = self.env.get(report_model_name)
report_model = self._get_rendering_context_model()
data = data and dict(data) or {}
@@ -5,7 +5,7 @@ import base64
import io
import odoo
from odoo.tests import common
from odoo.tests import common, tagged
from odoo.tools.misc import file_open, mute_logger
from odoo.tools.translate import _
@@ -23,6 +23,18 @@ class TestTermCount(common.TransactionCase):
])
self.assertEqual(len(ids), 2)
def test_count_term_module(self):
"""
Just make sure we have as many translation entries as we wanted and module deducted from file content
"""
odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr.po', 'fr_FR', verbose=False)
translations = self.env['ir.translation'].search([
('lang', '=', 'fr_FR'),
('src', '=', 'Ijkl'),
('module', '=', 'test_translation_import'),
])
self.assertEqual(len(translations), 1)
def test_noupdate(self):
"""
Make sure no update do not overwrite translations
@@ -167,3 +179,49 @@ class TestTermCount(common.TransactionCase):
self.env.context = dict(self.env.context, lang="dot")
self.assertEqual(_("Accounting"), "samva", "The code translation was not applied")
@tagged('post_install', '-at_install')
class TestTranslationFlow(common.TransactionCase):
def test_export_import(self):
""" Ensure export+import gives the same result as loading a language """
# load language and generate missing terms to create missing empty terms
with mute_logger('odoo.addons.base.models.ir_translation'):
self.env["base.language.install"].create({'lang': 'fr_FR'}).lang_install()
self.env["base.update.translations"].create({'lang': 'fr_FR'}).act_update()
translations = self.env["ir.translation"].search([
('lang', '=', 'fr_FR'),
('module', '=', 'test_translation_import')
])
# minus 3 as the original fr.po contains 3 fake code translations (cf
# test_no_duplicate test) which are not found by babel_extract_terms
init_translation_count = len(translations) - 3
module = self.env.ref('base.module_test_translation_import')
export = self.env["base.language.export"].create({
'lang': 'fr_FR',
'format': 'po',
'modules': [(6, 0, [module.id])]
})
export.act_getfile()
po_file = export.data
self.assertIsNotNone(po_file)
translations.unlink()
import_fr = self.env["base.language.import"].create({
'name': 'French',
'code': 'fr_FR',
'data': export.data,
'filename': export.name,
})
with mute_logger('odoo.addons.base.models.res_lang'):
import_fr.with_context(create_empty_translation=True).import_lang()
import_translation = self.env["ir.translation"].search([
('lang', '=', 'fr_FR'),
('module', '=', 'test_translation_import')
])
self.assertEqual(init_translation_count, len(import_translation))
+6 -4
View File
@@ -509,7 +509,7 @@ class PoFile(object):
return self.lines_count - len(self.lines)
def next(self):
trans_type = name = res_id = source = trad = None
trans_type = name = res_id = source = trad = module = None
if self.extra_lines:
trans_type, name, res_id, source, trad, comments = self.extra_lines.pop(0)
if not res_id:
@@ -530,6 +530,8 @@ class PoFile(object):
line = line[2:].strip()
if not line.startswith('module:'):
comments.append(line)
else:
module = line[7:].strip()
elif line.startswith('#:'):
# Process the `reference` comments. Each line can specify
# multiple targets (e.g. model, view, code, selection,
@@ -603,7 +605,7 @@ class PoFile(object):
_logger.warning('Missing "#:" formatted comment at line %d for the following source:\n\t%s',
self.cur_line(), source[:30])
return next(self)
return trans_type, name, res_id, source, trad, '\n'.join(comments)
return trans_type, name, res_id, source, trad, '\n'.join(comments), module
__next__ = next
def write_infos(self, modules):
@@ -1045,7 +1047,7 @@ def trans_load_data(cr, fileobj, fileformat, lang, lang_name=None, verbose=True,
elif fileformat == 'po':
reader = PoFile(fileobj)
fields = ['type', 'name', 'res_id', 'src', 'value', 'comments']
fields = ['type', 'name', 'res_id', 'src', 'value', 'comments', 'module']
# Make a reader for the POT file and be somewhat defensive for the
# stable branch.
@@ -1079,7 +1081,7 @@ def trans_load_data(cr, fileobj, fileformat, lang, lang_name=None, verbose=True,
self.comments = None
pot_targets = defaultdict(Target)
for type, name, res_id, src, _ignored, comments in pot_reader:
for type, name, res_id, src, _ignored, comments, module in pot_reader:
if type is not None:
target = pot_targets[src]
target.targets.add((type, name, type != 'code' and res_id or 0))