[PATCH] Upstream patch - 23092022

This commit is contained in:
Parthiv Patel
2022-09-23 08:34:47 +00:00
parent 1a14569fd4
commit caf97143e8
20 changed files with 351 additions and 46 deletions

View File

@@ -1484,10 +1484,12 @@ class AccountMove(models.Model):
if new_pmt_state == 'paid' and move.move_type in ('in_invoice', 'out_invoice', 'entry'):
reverse_type = move.move_type == 'in_invoice' and 'in_refund' or move.move_type == 'out_invoice' and 'out_refund' or 'entry'
reverse_moves = self.env['account.move'].search([('reversed_entry_id', '=', move.id), ('state', '=', 'posted'), ('move_type', '=', reverse_type)])
caba_moves = self.env['account.move'].search([('tax_cash_basis_move_id', 'in', move.ids + reverse_moves.ids), ('state', '=', 'posted')])
# We only set 'reversed' state in cas of 1 to 1 full reconciliation with a reverse entry; otherwise, we use the regular 'paid' state
# We ignore potentials cash basis moves reconciled because the transition account of the tax is reconcilable
reverse_moves_full_recs = reverse_moves.mapped('line_ids.full_reconcile_id')
if reverse_moves_full_recs.mapped('reconciled_line_ids.move_id').filtered(lambda x: x not in (reverse_moves + reverse_moves_full_recs.mapped('exchange_move_id'))) == move:
if reverse_moves_full_recs.mapped('reconciled_line_ids.move_id').filtered(lambda x: x not in (caba_moves + reverse_moves + reverse_moves_full_recs.mapped('exchange_move_id'))) == move:
new_pmt_state = 'reversed'
move.payment_state = new_pmt_state

View File

@@ -711,7 +711,7 @@ class Meeting(models.Model):
current_attendees = self.filtered('active').attendee_ids
if 'partner_ids' in values:
(current_attendees - previous_attendees)._send_mail_to_attendees('calendar.calendar_template_meeting_invitation')
if 'start' in values:
if not self.env.context.get('is_calendar_event_new') and 'start' in values:
start_date = fields.Datetime.to_datetime(values.get('start'))
# Only notify on future events
if start_date and start_date >= fields.Datetime.now():
@@ -721,6 +721,9 @@ class Meeting(models.Model):
@api.model_create_multi
def create(self, vals_list):
# Prevent sending update notification when _inverse_dates is called
self = self.with_context(is_calendar_event_new=True)
vals_list = [ # Else bug with quick_create when we are filter on an other user
dict(vals, user_id=self.env.user.id) if not 'user_id' in vals else vals
for vals in vals_list
@@ -785,7 +788,7 @@ class Meeting(models.Model):
if len(event.alarm_ids) > 0:
self.env['calendar.alarm_manager']._notify_next_alarm(event.partner_ids.ids)
return events
return events.with_context(is_calendar_event_new=False)
def _read(self, fields):
if self.env.is_system():

View File

@@ -30,6 +30,20 @@ class TestEventNotifications(SavepointCase, MailCase):
}):
self.event.partner_ids = self.partner
def test_message_invite_allday(self):
with self.assertSinglePostNotifications([{'partner': self.partner, 'type': 'inbox'}], {
'message_type': 'user_notification',
'subtype': 'mail.mt_note',
}):
self.env['calendar.event'].with_context(mail_create_nolog=True).create([{
'name': 'Meeting',
'allday': True,
'start_date': fields.Date.today() + relativedelta(days=7),
'stop_date': fields.Date.today() + relativedelta(days=8),
'partner_ids': [(4, self.partner.id)],
}])
def test_message_invite_self(self):
with self.assertNoNotifications():
self.event.with_user(self.user).partner_ids = self.partner

View File

@@ -1163,6 +1163,40 @@
}),
]"/>
</record>
<record id="account_tax_template_oss_s_iva_ns" model="account.tax.template">
<field name="description">No sujeto y acogidas a la OSS (Servicios)</field>
<field name="type_tax_use">sale</field>
<field name="name">No sujeto y acogidas a la OSS (Servicios)</field>
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
<field name="amount" eval="0"/>
<field name="amount_type">percent</field>
<field name="tax_group_id" ref="tax_group_iva_0"/>
<field name="invoice_repartition_line_ids" eval="[(5, 0, 0),
(0,0, {
'factor_percent': 100,
'repartition_type': 'base',
'tag_ids': [ref('mod_303_123')],
}),
(0,0, {
'factor_percent': 100,
'repartition_type': 'tax',
}),
]"/>
<field name="refund_repartition_line_ids" eval="[(5, 0, 0),
(0,0, {
'factor_percent': 100,
'repartition_type': 'base',
'tag_ids': [ref('mod_303_123')],
}),
(0,0, {
'factor_percent': 100,
'repartition_type': 'tax',
}),
]"/>
</record>
<record id="account_tax_template_s_iva_ns_b" model="account.tax.template">
<field name="description">No sujeto (Bienes)</field>
<field name="type_tax_use">sale</field>
@@ -1197,6 +1231,40 @@
}),
]"/>
</record>
<record id="account_tax_template_oss_s_iva_ns_b" model="account.tax.template">
<field name="description">No sujeto y acogidas a la OSS (Bienes)</field>
<field name="type_tax_use">sale</field>
<field name="name">No sujeto y acogidas a la OSS (Bienes)</field>
<field name="chart_template_id" ref="l10n_es.account_chart_template_common"/>
<field name="amount" eval="0"/>
<field name="amount_type">percent</field>
<field name="tax_group_id" ref="tax_group_iva_0"/>
<field name="invoice_repartition_line_ids" eval="[(5, 0, 0),
(0,0, {
'factor_percent': 100,
'repartition_type': 'base',
'tag_ids': [ref('mod_303_123')],
}),
(0,0, {
'factor_percent': 100,
'repartition_type': 'tax',
}),
]"/>
<field name="refund_repartition_line_ids" eval="[(5, 0, 0),
(0,0, {
'factor_percent': 100,
'repartition_type': 'base',
'tag_ids': [ref('mod_303_123')],
}),
(0,0, {
'factor_percent': 100,
'repartition_type': 'tax',
}),
]"/>
</record>
<record id="account_tax_template_s_iva_e" model="account.tax.template">
<field name="description">Extracomunitario (Servicios)</field>
<field name="type_tax_use">sale</field>

View File

@@ -199,7 +199,7 @@ EU_TAG_MAP = {
},
# Spain
'l10n_es.account_chart_template_common': {
'invoice_base_tag': None,
'invoice_base_tag': "l10n_es.mod_303_124",
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,

View File

@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
from flectra import api, fields, models, _
from .eu_tax_map import EU_TAX_MAP
from flectra import api, models
from .eu_tag_map import EU_TAG_MAP
from .eu_tax_map import EU_TAX_MAP
class Company(models.Model):
@@ -117,7 +117,7 @@ class Company(models.Model):
return self.env.ref(f'l10n_eu_service.oss_tax_account_company_{self.id}')
def _get_oss_tags(self):
[chart_template_xml_id] = self.chart_template_id.get_xml_id().values()
[chart_template_xml_id] = self.chart_template_id.parent_id.get_xml_id().values() or self.chart_template_id.get_xml_id().values()
tag_for_country = EU_TAG_MAP.get(chart_template_xml_id, {
'invoice_base_tag': None,
'invoice_tax_tag': None,
@@ -125,11 +125,11 @@ class Company(models.Model):
'refund_tax_tag': None,
})
return {
repartition_line_key: (
self.env.ref(tag_xml_id).tag_ids.filtered(lambda t: not t.tax_negate)
if tag_xml_id
else None
)
for repartition_line_key, tag_xml_id in tag_for_country.items()
}
mapping = {}
for repartition_line_key, tag_xml_id in tag_for_country.items():
tag = self.env.ref(tag_xml_id) if tag_xml_id else None
if tag and tag._name == "account.tax.report.line":
tag = tag.tag_ids.filtered(lambda t: not t.tax_negate)
mapping[repartition_line_key] = tag
return mapping

View File

@@ -21,6 +21,10 @@ class TestOSSBelgium(AccountTestInvoicingCommon):
self.company_data['company']._map_eu_taxes()
def test_country_tag_from_belgium(self):
"""
This test ensure that xml_id from `account.tax.report.line` in the EU_TAG_MAP are processed correctly by the oss
tax creation mechanism.
"""
# get an eu country which isn't the current one:
another_eu_country_code = (self.env.ref('base.europe').country_ids - self.company_data['company'].country_id)[0].code
tax_oss = self.env['account.tax'].search([('name', 'ilike', f'%{another_eu_country_code}%')], limit=1)
@@ -41,6 +45,43 @@ class TestOSSBelgium(AccountTestInvoicingCommon):
self.assertIn(expected_tag_id, oss_tag_id, f"{doc_type} tag from Belgian CoA not correctly linked")
@tagged('post_install', 'post_install_l10n', '-at_install')
class TestOSSSpain(AccountTestInvoicingCommon):
@classmethod
def setUpClass(cls, chart_template_ref='l10n_es.account_chart_template_common'):
try:
super().setUpClass(chart_template_ref=chart_template_ref)
except ValueError as e:
if e.args[0] == "External ID not found in the system: l10n_es.account_chart_template_data":
cls.skipTest(cls, reason="Spanish CoA is required for this testSuite but l10n_es isn't installed")
else:
raise e
cls.company_data['company'].country_id = cls.env.ref('base.es')
cls.company_data['company']._map_eu_taxes()
def test_country_tag_from_spain(self):
"""
This test ensure that xml_id from `account.account.tag` in the EU_TAG_MAP are processed correctly by the oss
tax creation mechanism.
"""
# get an eu country which isn't the current one:
another_eu_country_code = (self.env.ref('base.europe').country_ids - self.company_data['company'].country_id)[0].code
tax_oss = self.env['account.tax'].search([('name', 'ilike', f'%{another_eu_country_code}%')], limit=1)
for doc_type, tag_xml_id in (
("invoice", "l10n_es.mod_303_124"),
):
with self.subTest(doc_type=doc_type, report_line_xml_id=tag_xml_id):
oss_tag_id = tax_oss[f"{doc_type}_repartition_line_ids"]\
.filtered(lambda x: x.repartition_type == 'base')\
.tag_ids
expected_tag_id = self.env.ref(tag_xml_id)
self.assertIn(expected_tag_id, oss_tag_id, f"{doc_type} tag from Spanish CoA not correctly linked")
@tagged('post_install', 'post_install_l10n', '-at_install')
class TestOSSUSA(AccountTestInvoicingCommon):
@@ -58,7 +99,6 @@ class TestOSSUSA(AccountTestInvoicingCommon):
self.assertFalse(len(tax_oss), "OSS tax shouldn't be instanced on a US company")
@tagged('post_install', 'post_install_l10n', '-at_install')
class TestOSSMap(AccountTestInvoicingCommon):

View File

@@ -94,14 +94,13 @@ class AccountMove(models.Model):
lines = self.invoice_line_ids.filtered(lambda l: not l.display_type)
for num, line in enumerate(lines):
price_subtotal = line.balance if convert_to_euros else line.price_subtotal
# The price_subtotal should be negative when:
sign = -1 if line.move_id.is_inbound() else 1
price_subtotal = (line.balance * sign) if convert_to_euros else line.price_subtotal
# The price_subtotal should be inverted when:
# The line has downpayment lines, but is not a downpayment (i.e. the final invoice, from which downpayment lines are subtracted) or,
# the line is a reverse charge refund.
if (line._get_downpayment_lines() and not is_downpayment) or reverse_charge_refund:
price_subtotal = -abs(price_subtotal)
else:
price_subtotal = abs(price_subtotal)
price_subtotal = -price_subtotal
# Unit price
price_unit = 0

View File

@@ -309,6 +309,22 @@ class TestItEdi(AccountEdiTestCommon):
],
})
cls.negative_price_invoice = cls.env['account.move'].with_company(cls.company).create({
'move_type': 'out_invoice',
'invoice_date': datetime.date(2022, 3, 24),
'partner_id': cls.italian_partner_a.id,
'invoice_line_ids': [
(0, 0, {
**cls.standard_line,
}),
(0, 0, {
**cls.standard_line,
'name': 'negative_line',
'price_unit': -100.0,
}),
],
})
# post the invoices
cls.price_included_invoice._post()
cls.partial_discount_invoice._post()
@@ -318,6 +334,7 @@ class TestItEdi(AccountEdiTestCommon):
cls.total_400_VAT_simplified_invoice._post()
cls.pa_partner_invoice._post()
cls.zero_tax_invoice._post()
cls.negative_price_invoice._post()
cls.edi_basis_xml = cls._get_test_file_content('IT00470550013_basis.xml')
cls.edi_simplified_basis_xml = cls._get_test_file_content('IT00470550013_simpl.xml')
@@ -668,3 +685,44 @@ class TestItEdi(AccountEdiTestCommon):
)
invoice_etree = self.with_applied_xpath(invoice_etree, "<xpath expr='.//Allegati' position='replace'/>")
self.assertXmlTreeEqual(invoice_etree, expected_etree)
def test_negative_price_invoice(self):
invoice_etree = etree.fromstring(self.negative_price_invoice._export_as_xml())
expected_etree = self.with_applied_xpath(
etree.fromstring(self.edi_basis_xml),
'''
<xpath expr="//FatturaElettronicaBody//DatiBeniServizi" position="replace">
<DatiBeniServizi>
<DettaglioLinee>
<NumeroLinea>1</NumeroLinea>
<Descrizione>standard_line</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>800.400000</PrezzoUnitario>
<PrezzoTotale>800.40</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DettaglioLinee>
<NumeroLinea>2</NumeroLinea>
<Descrizione>negative_line</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>-100.000000</PrezzoUnitario>
<PrezzoTotale>-100.00</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DatiRiepilogo>
<AliquotaIVA>22.00</AliquotaIVA>
<ImponibileImporto>700.40</ImponibileImporto>
<Imposta>154.09</Imposta>
<EsigibilitaIVA>I</EsigibilitaIVA>
</DatiRiepilogo>
</DatiBeniServizi>
</xpath>
<xpath expr="//DettaglioPagamento//ImportoPagamento" position="inside">
854.49
</xpath>
<xpath expr="//DatiGeneraliDocumento//ImportoTotaleDocumento" position="inside">
854.49
</xpath>
''')
invoice_etree = self.with_applied_xpath(invoice_etree, "<xpath expr='.//Allegati' position='replace'/>")
self.assertXmlTreeEqual(invoice_etree, expected_etree)

View File

@@ -31,11 +31,11 @@ class AccountMove(models.Model):
invoice_line_pickings.setdefault(done_moves_related.picking_id, []).append(line_count)
else:
total_invoices = done_moves_related.mapped('sale_line_id.invoice_lines').filtered(
lambda l: l.move_id.state == 'posted' and l.move_id.move_type == 'out_invoice').sorted(lambda l: l.move_id.invoice_date)
lambda l: l.move_id.state == 'posted' and l.move_id.move_type == 'out_invoice').sorted(lambda l: (l.move_id.invoice_date, l.move_id.id))
total_invs = [(i.product_uom_id._compute_quantity(i.quantity, i.product_id.uom_id), i) for i in total_invoices]
inv = total_invs.pop(0)
# Match all moves and related invoice lines FIFO looking for when the matched invoice_line matches line
for move in done_moves_related.sorted(lambda m: m.date):
for move in done_moves_related.sorted(lambda m: (m.date, m.id)):
rounding = move.product_uom.rounding
move_qty = move.product_qty
while (float_compare(move_qty, 0, precision_rounding=rounding) > 0):

View File

@@ -88,3 +88,49 @@ class TestDDT(TestSaleCommon):
self.inv2 = self.so._create_invoices()
self.inv2.action_post()
self.assertEqual(self.inv2.l10n_it_ddt_ids.ids, (pickx1 | pickx2).ids, 'DDTs should be linked to the invoice')
def test_ddt_flow_2(self):
"""
Test that the link between the invoice lines and the deliveries linked to the invoice
through the link with the sale order is calculated correctly.
"""
so = self.env['sale.order'].create({
'partner_id': self.partner_a.id,
'order_line': [(0, 0, {
'product_id': self.product_a.id,
'product_uom_qty': 3,
'product_uom': self.product_a.uom_id.id,
'price_unit': self.product_a.list_price,
'tax_id': self.company_data['default_tax_sale']
}
)],
'pricelist_id': self.company_data['default_pricelist'].id,
'picking_policy': 'direct',
})
so.action_confirm()
# deliver partially
picking_1 = so.picking_ids
picking_1.move_lines.write({'quantity_done': 1})
wiz_act = picking_1.button_validate()
wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
wiz.process()
invoice_1 = so._create_invoices()
invoice_form = Form(invoice_1)
with invoice_form.invoice_line_ids.edit(0) as line:
line.quantity = 1.0
invoice_1 = invoice_form.save()
invoice_1.action_post()
picking_2 = so.picking_ids.filtered(lambda p: p.state != 'done')
picking_2.move_lines.write({'quantity_done': 2})
picking_2.button_validate()
invoice_2 = so._create_invoices()
invoice_2.action_post()
# Invalidate the cache to ensure the lines will be fetched in the right order.
picking_2.invalidate_cache()
self.assertEqual(invoice_1.l10n_it_ddt_ids.ids, picking_1.ids, 'DDT picking_1 should be linked to the invoice_1')
self.assertEqual(invoice_2.l10n_it_ddt_ids.ids, picking_2.ids, 'DDT picking_2 should be linked to the invoice_2')

View File

@@ -374,15 +374,6 @@ class MrpWorkorder(models.Model):
for workorder in self:
workorder.scrap_count = count_data.get(workorder.id, 0)
@api.onchange('date_planned_finished')
def _onchange_date_planned_finished(self):
if self.date_planned_start and self.date_planned_finished:
interval = self.workcenter_id.resource_calendar_id.get_work_duration_data(
self.date_planned_start, self.date_planned_finished,
domain=[('time_type', 'in', ['leave', 'other'])]
)
self.duration_expected = interval['hours'] * 60
@api.onchange('operation_id')
def _onchange_operation_id(self):
if self.operation_id:
@@ -392,10 +383,25 @@ class MrpWorkorder(models.Model):
@api.onchange('date_planned_start', 'duration_expected', 'workcenter_id')
def _onchange_date_planned_start(self):
if self.date_planned_start and self.duration_expected and self.workcenter_id:
self.date_planned_finished = self.workcenter_id.resource_calendar_id.plan_hours(
self.duration_expected / 60.0, self.date_planned_start,
compute_leaves=True, domain=[('time_type', 'in', ['leave', 'other'])]
)
self.date_planned_finished = self._calculate_date_planned_finished()
def _calculate_date_planned_finished(self, date_planned_start=False):
return self.workcenter_id.resource_calendar_id.plan_hours(
self.duration_expected / 60.0, date_planned_start or self.date_planned_start,
compute_leaves=True, domain=[('time_type', 'in', ['leave', 'other'])]
)
@api.onchange('date_planned_finished')
def _onchange_date_planned_finished(self):
if self.date_planned_start and self.date_planned_finished:
self.duration_expected = self._calculate_duration_expected()
def _calculate_duration_expected(self, date_planned_start=False, date_planned_finished=False):
interval = self.workcenter_id.resource_calendar_id.get_work_duration_data(
date_planned_start or self.date_planned_start, date_planned_finished or self.date_planned_finished,
domain=[('time_type', 'in', ['leave', 'other'])]
)
return interval['hours'] * 60
@api.onchange('operation_id', 'workcenter_id', 'qty_production')
def _onchange_expected_duration(self):
@@ -416,6 +422,16 @@ class MrpWorkorder(models.Model):
end_date = fields.Datetime.to_datetime(values.get('date_planned_finished')) or workorder.date_planned_finished
if start_date and end_date and start_date > end_date:
raise UserError(_('The planned end date of the work order cannot be prior to the planned start date, please correct this to save the work order.'))
if 'duration_expected' not in values and not self.env.context.get('bypass_duration_calculation'):
if values.get('date_planned_start') and values.get('date_planned_finished'):
computed_finished_time = self._calculate_date_planned_finished(start_date)
values['date_planned_finished'] = computed_finished_time
elif values.get('date_planned_start'):
computed_duration = self._calculate_duration_expected(date_planned_start=start_date)
values['duration_expected'] = computed_duration
elif values.get('date_planned_finished'):
computed_duration = self._calculate_duration_expected(date_planned_finished=end_date)
values['duration_expected'] = computed_duration
# Update MO dates if the start date of the first WO or the
# finished date of the last WO is update.
if workorder == workorder.production_id.workorder_ids[0] and 'date_planned_start' in values:
@@ -582,7 +598,7 @@ class MrpWorkorder(models.Model):
vals['date_planned_start'] = start_date
if self.date_planned_finished and self.date_planned_finished < start_date:
vals['date_planned_finished'] = start_date
return self.write(vals)
return self.with_context(bypass_duration_calculation=True).write(vals)
def button_finish(self):
end_date = datetime.now()

View File

@@ -351,6 +351,7 @@
<field name="qty_production"/>
<field name="product_uom_id" force_save="1"/>
<field name="consumption"/>
<field name="operation_id"/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_global_click">

View File

@@ -56,7 +56,7 @@ class ProductProduct(models.Model):
for move in moves_list:
value += move.product_qty * move.product_id._compute_average_price(qty_invoiced * move.product_qty, qty_to_invoice * move.product_qty, move)
continue
line_qty = bom_line.product_uom_id._compute_quantity(bom_line.product_qty, bom_line.product_id.uom_id)
line_qty = bom_line.product_uom_id._compute_quantity(bom_lines[bom_line]['qty'], bom_line.product_id.uom_id)
moves = self.env['stock.move'].concat(*moves_list)
value += line_qty * bom_line.product_id._compute_average_price(qty_invoiced * line_qty, qty_to_invoice * line_qty, moves)
return value

View File

@@ -2233,3 +2233,46 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
price = line.product_id.with_company(line.company_id)._compute_average_price(0, line.product_uom_qty, line.move_ids)
self.assertEqual(price, 10)
def test_kit_cost_calculation(self):
""" Check that the average cost price is computed correctly after SO confirmation:
BOM 1:
- 1 unit of “super kit”:
- 2 units of “component a”
BOM 2:
- 1 unit of “component a”:
- 3 units of "component b"
1 unit of "component b" = $10
1 unit of "super kit" = 2 * 3 * $10 = *$60
"""
super_kit = self._cls_create_product('Super Kit', self.uom_unit)
(super_kit + self.component_a + self.component_b).categ_id.property_cost_method = 'average'
self.env['mrp.bom'].create({
'product_tmpl_id': self.component_a.product_tmpl_id.id,
'product_qty': 1.0,
'type': 'phantom',
'bom_line_ids': [(0, 0, {
'product_id': self.component_b.id,
'product_qty': 3.0,
})]
})
self.env['mrp.bom'].create({
'product_tmpl_id': super_kit.product_tmpl_id.id,
'product_qty': 1.0,
'type': 'phantom',
'bom_line_ids': [(0, 0, {
'product_id': self.component_a.id,
'product_qty': 2.0,
})]
})
self.component_b.standard_price = 10
self.component_a.button_bom_cost()
super_kit.button_bom_cost()
so_form = Form(self.env['sale.order'])
so_form.partner_id = self.partner_a
with so_form.order_line.new() as line:
line.product_id = super_kit
so = so_form.save()
self.assertEqual(so.order_line.purchase_price, 60)
so.action_confirm()
self.assertEqual(so.order_line.purchase_price, 60)

View File

@@ -122,6 +122,9 @@ tour.register('sale_matrix_tour', {
extra_trigger: '.oe_subtotal_footer_separator:contains("65.32")',
}, {
trigger: '.o_form_button_save:contains("Save")',
}, {
trigger: '.o_form_button_edit:contains("Edit")',
run: function () {}, // Ensure the form is saved before closing the browser
},
]);

View File

@@ -29,7 +29,7 @@ class AccountMove(models.Model):
if self.state == 'draft' or not self.invoice_date or self.move_type not in ('out_invoice', 'out_refund'):
return []
current_invoice_amls = self.invoice_line_ids.filtered(lambda aml: not aml.display_type and aml.product_id and aml.quantity)
current_invoice_amls = self.invoice_line_ids.filtered(lambda aml: not aml.display_type and aml.product_id and aml.product_id.type in ('consu', 'product') and aml.quantity)
all_invoices_amls = current_invoice_amls.sale_line_ids.invoice_lines.filtered(lambda aml: aml.move_id.state == 'posted').sorted(lambda aml: (aml.date, aml.move_name, aml.id))
index = all_invoices_amls.ids.index(current_invoice_amls[:1].id) if current_invoice_amls[:1] in all_invoices_amls else 0
previous_amls = all_invoices_amls[:index]

View File

@@ -18,7 +18,7 @@ options.registry.gallery = options.Class.extend({
var self = this;
// Make sure image previews are updated if images are changed
this.$target.on('image_changed', 'img', function (ev) {
this.$target.on('image_changed.gallery', 'img', function (ev) {
var $img = $(ev.currentTarget);
var index = self.$target.find('.carousel-item.active').index();
self.$('.carousel:first li[data-target]:eq(' + index + ')')
@@ -27,12 +27,12 @@ options.registry.gallery = options.Class.extend({
// When the snippet is empty, an edition button is the default content
// TODO find a nicer way to do that to have editor style
this.$target.on('click', '.o_add_images', function (e) {
this.$target.on('click.gallery', '.o_add_images', function (e) {
e.stopImmediatePropagation();
self.addImages(false);
});
this.$target.on('dropped', 'img', function (ev) {
this.$target.on('dropped.gallery', 'img', function (ev) {
self.mode(null, self.getMode());
if (!ev.target.height) {
$(ev.target).one('load', function () {
@@ -74,6 +74,13 @@ options.registry.gallery = options.Class.extend({
this.$target.removeAttr('style');
}
},
/**
* @override
*/
destroy() {
this._super(...arguments);
this.$target.off('.gallery');
},
//--------------------------------------------------------------------------
// Options

View File

@@ -718,7 +718,7 @@ class WebsiteSale(http.Controller):
return request.redirect('/shop/checkout')
# IF POSTED
if 'submitted' in kw:
if 'submitted' in kw and request.httprequest.method == "POST":
pre_values = self.values_preprocess(order, mode, kw)
errors, error_msg = self.checkout_form_validate(mode, kw, pre_values)
post, errors, error_msg = self.values_postprocess(order, mode, pre_values, errors, error_msg)

View File

@@ -142,7 +142,8 @@ class TestWebsiteSaleCheckoutAddress(TransactionCaseWithUserDemo):
p = self.env.user.partner_id
so = self._create_so(p.id)
with MockRequest(self.env, website=self.website, sale_order_id=so.id):
with MockRequest(self.env, website=self.website, sale_order_id=so.id) as req:
req.httprequest.method = "POST"
self.WebsiteSaleController.address(**self.default_address_values)
self.assertFalse(self._get_last_address(p).website_id, "New shipping address should not have a website set on it (no specific_user_account).")
@@ -186,7 +187,9 @@ class TestWebsiteSaleCheckoutAddress(TransactionCaseWithUserDemo):
env = api.Environment(self.env.cr, self.demo_user.id, {})
# change also website env for `sale_get_order` to not change order partner_id
with MockRequest(env, website=self.website.with_env(env), sale_order_id=so.id):
with MockRequest(env, website=self.website.with_env(env), sale_order_id=so.id) as req:
req.httprequest.method = "POST"
# 1. Logged in user, new shipping
self.WebsiteSaleController.address(**self.default_address_values)
new_shipping = self._get_last_address(self.demo_partner)
@@ -207,7 +210,9 @@ class TestWebsiteSaleCheckoutAddress(TransactionCaseWithUserDemo):
env = api.Environment(self.env.cr, self.website.user_id.id, {})
# change also website env for `sale_get_order` to not change order partner_id
with MockRequest(env, website=self.website.with_env(env), sale_order_id=so.id):
with MockRequest(env, website=self.website.with_env(env), sale_order_id=so.id) as req:
req.httprequest.method = "POST"
# 1. Public user, new billing
self.default_address_values['partner_id'] = -1
self.WebsiteSaleController.address(**self.default_address_values)