mirror of
https://gitlab.com/flectra-hq/flectra.git
synced 2026-10-26 07:50:05 -05:00
[PATCH] Upstream patch - 08112022
This commit is contained in:
1 parent
a610488738
commit
83f1d94de1
19 files changed
+119
-25
No files matched your search
@@ -293,7 +293,7 @@ class AccountMove(models.Model):
|
||||
invoice_incoterm_id = fields.Many2one('account.incoterms', string='Incoterm',
|
||||
default=_get_default_invoice_incoterm,
|
||||
help='International Commercial Terms are a series of predefined commercial terms used in international transactions.')
|
||||
display_qr_code = fields.Boolean(string="Display QR-code", related='company_id.qr_code')
|
||||
display_qr_code = fields.Boolean(string="Display QR-code", compute='_compute_display_qr_code')
|
||||
qr_code_method = fields.Selection(string="Payment QR-code", copy=False,
|
||||
selection=lambda self: self.env['res.partner.bank'].get_available_qr_methods_in_sequence(),
|
||||
help="Type of QR-code to be generated for the payment of this invoice, when printing it. If left blank, the first available and usable method will be used.")
|
||||
@@ -2999,6 +2999,14 @@ class AccountMove(models.Model):
|
||||
for move in self:
|
||||
move.has_reconciled_entries = len(move.line_ids._reconciled_lines()) > 1
|
||||
|
||||
@api.depends('company_id')
|
||||
def _compute_display_qr_code(self):
|
||||
for record in self:
|
||||
record.display_qr_code = (
|
||||
record.move_type in ('out_invoice', 'out_receipt')
|
||||
and record.company_id.qr_code
|
||||
)
|
||||
|
||||
def action_view_reverse_entry(self):
|
||||
self.ensure_one()
|
||||
|
||||
@@ -3074,8 +3082,8 @@ class AccountMove(models.Model):
|
||||
"""
|
||||
self.ensure_one()
|
||||
|
||||
if not self.is_invoice():
|
||||
raise UserError(_("QR-codes can only be generated for invoice entries."))
|
||||
if not self.display_qr_code:
|
||||
return None
|
||||
|
||||
qr_code_method = self.qr_code_method
|
||||
if qr_code_method:
|
||||
|
||||
@@ -996,7 +996,7 @@
|
||||
sum="Total Credit"
|
||||
attrs="{'invisible': [('display_type', 'in', ('line_section', 'line_note'))]}"/>
|
||||
<field name="tax_fiscal_country_id" invisible="1"/>
|
||||
<field name="tax_tag_ids" widget="many2many_tags" string="Tax Grids" optional="show" domain="[('country_id', '=', tax_fiscal_country_id), ('applicability', '=', 'taxes')]"/>
|
||||
<field name="tax_tag_ids" widget="many2many_tags" string="Tax Grids" optional="show" domain="[('country_id', '=', tax_fiscal_country_id), ('applicability', '=', 'taxes')]" options="{'no_create': True}"/>
|
||||
|
||||
<!-- Buttons -->
|
||||
<button name="action_automatic_entry"
|
||||
|
||||
@@ -393,7 +393,7 @@ class AccountPaymentRegister(models.TransientModel):
|
||||
wizard.amount = wizard.source_amount
|
||||
else:
|
||||
# Foreign currency on payment different than the one set on the journal entries.
|
||||
amount_payment_currency = wizard.company_id.currency_id._convert(wizard.source_amount, wizard.currency_id, wizard.company_id, wizard.payment_date)
|
||||
amount_payment_currency = wizard.company_id.currency_id._convert(wizard.source_amount, wizard.currency_id, wizard.company_id, wizard.payment_date or fields.Date.today())
|
||||
wizard.amount = amount_payment_currency
|
||||
|
||||
@api.depends('amount')
|
||||
@@ -407,7 +407,7 @@ class AccountPaymentRegister(models.TransientModel):
|
||||
wizard.payment_difference = wizard.source_amount - wizard.amount
|
||||
else:
|
||||
# Foreign currency on payment different than the one set on the journal entries.
|
||||
amount_payment_currency = wizard.company_id.currency_id._convert(wizard.source_amount, wizard.currency_id, wizard.company_id, wizard.payment_date)
|
||||
amount_payment_currency = wizard.company_id.currency_id._convert(wizard.source_amount, wizard.currency_id, wizard.company_id, wizard.payment_date or fields.Date.today())
|
||||
wizard.payment_difference = amount_payment_currency - wizard.amount
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@@ -60,6 +60,7 @@ _ref_vat = {
|
||||
'nl': 'NL123456782B90',
|
||||
'no': 'NO123456785',
|
||||
'pe': '10XXXXXXXXY or 20XXXXXXXXY or 15XXXXXXXXY or 16XXXXXXXXY or 17XXXXXXXXY',
|
||||
'ph': '123-456-789-01234',
|
||||
'pl': 'PL1234567883',
|
||||
'pt': 'PT123456789',
|
||||
'ro': 'RO1234567897',
|
||||
|
||||
@@ -474,6 +474,7 @@ class Meeting(models.Model):
|
||||
for attendee in meeting.attendee_ids:
|
||||
attendee_add = event.add('attendee')
|
||||
attendee_add.value = u'MAILTO:' + (attendee.email or u'')
|
||||
event.add('organizer').value = u'MAILTO:' + (meeting.user_id.email or u'')
|
||||
result[meeting.id] = cal.serialize().encode('utf-8')
|
||||
|
||||
return result
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<record id="sms_template_data_calendar_reminder" model="sms.template">
|
||||
<field name="name">Calendar Event: Reminder</field>
|
||||
<field name="model_id" ref="calendar.model_calendar_event"/>
|
||||
<field name="body">Event reminder: ${object.name}, ${object.display_time}</field>
|
||||
<field name="body">Event reminder: ${object.name}, ${object.get_display_time_tz(object.partner_id.tz)}</field>
|
||||
</record>
|
||||
</data>
|
||||
</flectra>
|
||||
@@ -166,7 +166,7 @@
|
||||
<ImponibileImporto t-esc="format_monetary(tax_dict['base_amount'] if currency.name == 'EUR' else tax_dict['base_amount_currency'], currency)"/>
|
||||
<Imposta t-esc="format_monetary(tax_dict['tax_amount'] if currency.name == 'EUR' else tax_dict['base_amount_currency'], currency)"/>
|
||||
<EsigibilitaIVA t-if="not has_exoneration or kind_exoneration == 'N6'" t-esc="tax.l10n_it_vat_due_date"/>
|
||||
<RiferimentoNormativo t-if="has_exoneration" t-esc="format_alphanumeric(tax.l10n_it_law_reference[:100])"/>
|
||||
<RiferimentoNormativo t-if="tax.l10n_it_law_reference" t-esc="format_alphanumeric(tax.l10n_it_law_reference[:100])"/>
|
||||
</DatiRiepilogo>
|
||||
</t>
|
||||
</DatiBeniServizi>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<field name="l10n_it_vat_due_date"/>
|
||||
<field name="l10n_it_has_exoneration" readonly="False"/>
|
||||
<field name="l10n_it_kind_exoneration" attrs="{'invisible': [('l10n_it_has_exoneration', '=', False)]}"/>
|
||||
<field name="l10n_it_law_reference" attrs="{'invisible': [('l10n_it_has_exoneration', '=', False)]}"/>
|
||||
<field name="l10n_it_law_reference"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</data>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<Anagrafica>
|
||||
<Denominazione>Alessi</Denominazione>
|
||||
</Anagrafica>
|
||||
<RegimeFiscale>RF01</RegimeFiscale>
|
||||
<RegimeFiscale>RF18</RegimeFiscale>
|
||||
</DatiAnagrafici>
|
||||
<Sede>
|
||||
<Indirizzo>Avenue Test rue </Indirizzo>
|
||||
|
||||
@@ -435,7 +435,7 @@ class StockMove(models.Model):
|
||||
|
||||
def _update_quantity_done(self, mo):
|
||||
self.ensure_one()
|
||||
new_qty = mo.product_uom_id._compute_quantity((mo.qty_producing - mo.qty_produced) * self.unit_factor, mo.product_uom_id, rounding_method='HALF-UP')
|
||||
new_qty = float_round((mo.qty_producing - mo.qty_produced) * self.unit_factor, precision_rounding=self.product_uom.rounding)
|
||||
if not self.is_quantity_done_editable:
|
||||
self.move_line_ids.filtered(lambda ml: ml.state not in ('done', 'cancel')).qty_done = 0
|
||||
self.move_line_ids = self._set_quantity_done_prepare_vals(new_qty)
|
||||
|
||||
@@ -2395,3 +2395,27 @@ class TestMrpOrder(TestMrpCommon):
|
||||
('raw_material_production_id', '=', mo_backorder.id)])
|
||||
self.assertEqual(sum(move_prod_1_bo.mapped('product_uom_qty')), 60.0)
|
||||
self.assertEqual(sum(move_prod_2_bo.mapped('product_uom_qty')), 40.0)
|
||||
|
||||
def test_update_qty_to_consume_of_component(self):
|
||||
"""
|
||||
The UoM of the finished product has a rounding precision equal to 1.0
|
||||
and the UoM of the component has a decimal one. When the producing qty
|
||||
is set, an onchange autocomplete the consumed quantity of the component.
|
||||
Then, when updating the 'to consume' quantity of the components, their
|
||||
consumed quantity is updated again. The test ensures that this update
|
||||
respects the rounding precisions
|
||||
"""
|
||||
self.uom_dozen.rounding = 1
|
||||
self.bom_4.product_uom_id = self.uom_dozen
|
||||
|
||||
mo_form = Form(self.env['mrp.production'])
|
||||
mo_form.bom_id = self.bom_4
|
||||
mo = mo_form.save()
|
||||
mo.action_confirm()
|
||||
|
||||
with Form(mo) as mo_form:
|
||||
mo_form.qty_producing = 1
|
||||
with mo_form.move_raw_ids.edit(0) as raw:
|
||||
raw.product_uom_qty = 1.25
|
||||
|
||||
self.assertEqual(mo.move_raw_ids.quantity_done, 1.25)
|
||||
@@ -393,6 +393,10 @@ class SaleOrder(models.Model):
|
||||
def update_line(order, lines, values):
|
||||
'''Update the lines and return them if they should be deleted'''
|
||||
lines_to_remove = self.env['sale.order.line']
|
||||
# move global coupons to the end of the SO
|
||||
if program.discount_apply_on == 'on_order':
|
||||
values['sequence'] = max(order.order_line.mapped('sequence')) + 1
|
||||
|
||||
# Check commit 6bb42904a03 for next if/else
|
||||
# Remove reward line if price or qty equal to 0
|
||||
if values['product_uom_qty'] and values['price_unit']:
|
||||
|
||||
@@ -52,3 +52,38 @@ class TestSaleInvoicing(TestSaleCouponCommon):
|
||||
self.assertEqual(order.order_line, invoiceable_lines)
|
||||
account_move = order._create_invoices()
|
||||
self.assertEqual(len(account_move.invoice_line_ids), 2)
|
||||
|
||||
def test_coupon_on_order_sequence(self):
|
||||
# discount_coupon_program
|
||||
self.env['coupon.program'].create({
|
||||
'name': '10% Discount', # Default behavior
|
||||
'program_type': 'coupon_program',
|
||||
'reward_type': 'discount',
|
||||
'discount_apply_on': 'on_order',
|
||||
'promo_code_usage': 'no_code_needed',
|
||||
})
|
||||
order = self.empty_order
|
||||
|
||||
# orderline1
|
||||
self.env['sale.order.line'].create({
|
||||
'product_id': self.env.ref('product.product_product_6').id,
|
||||
'name': 'largeCabinet',
|
||||
'product_uom_qty': 1.0,
|
||||
'order_id': order.id,
|
||||
})
|
||||
|
||||
order.recompute_coupon_lines()
|
||||
self.assertEqual(len(order.order_line), 2, 'Coupon correctly applied')
|
||||
|
||||
# orderline2
|
||||
self.env['sale.order.line'].create({
|
||||
'product_id': self.env.ref('product.product_product_11').id,
|
||||
'name': 'conferenceChair',
|
||||
'product_uom_qty': 1.0,
|
||||
'order_id': order.id,
|
||||
})
|
||||
|
||||
order.recompute_coupon_lines()
|
||||
self.assertEqual(len(order.order_line), 3, 'Coupon correctly applied')
|
||||
|
||||
self.assertTrue(order.order_line.sorted(lambda x: x.sequence)[-1].is_reward_line, 'Global coupons appear on the last line')
|
||||
@@ -13,7 +13,7 @@ from reportlab.pdfgen.canvas import Canvas
|
||||
|
||||
from flectra import fields, models, api, _
|
||||
from flectra.addons.iap.tools import iap_tools
|
||||
from flectra.exceptions import AccessError
|
||||
from flectra.exceptions import AccessError, UserError
|
||||
from flectra.tools.safe_eval import safe_eval
|
||||
|
||||
DEFAULT_ENDPOINT = 'https://iap-snailmail.flectrahq.com'
|
||||
@@ -140,7 +140,10 @@ class SnailmailLetter(models.Model):
|
||||
else:
|
||||
report_name = 'Document'
|
||||
filename = "%s.%s" % (report_name, "pdf")
|
||||
pdf_bin, _ = report.with_context(snailmail_layout=not self.cover)._render_qweb_pdf(self.res_id)
|
||||
paperformat = report.get_paperformat()
|
||||
if (paperformat.format == 'custom' and paperformat.page_width != 210 and paperformat.page_height != 297) or paperformat.format != 'A4':
|
||||
raise UserError(_("Please use an A4 Paper format."))
|
||||
pdf_bin, unused_filetype = report.with_context(snailmail_layout=not self.cover, lang='en_US').render_qweb_pdf(self.res_id)
|
||||
if self.cover:
|
||||
pdf_bin = self._append_cover_page(pdf_bin)
|
||||
attachment = self.env['ir.attachment'].create({
|
||||
@@ -426,7 +429,7 @@ class SnailmailLetter(models.Model):
|
||||
return all(record[key] for key in required_keys)
|
||||
|
||||
def _append_cover_page(self, invoice_bin: bytes):
|
||||
address = self.partner_id.with_context(show_address=True)._get_name().replace('\n', '<br/>')
|
||||
address = self.partner_id.with_context(show_address=True, lang='en_US')._get_name().replace('\n', '<br/>')
|
||||
address_x = 118 * mm
|
||||
address_y = 60 * mm
|
||||
frame_width = 85.5 * mm
|
||||
|
||||
@@ -272,11 +272,11 @@ class StockMove(models.Model):
|
||||
|
||||
|
||||
for svl in stock_valuation_layers:
|
||||
if not svl.product_id.valuation == 'real_time':
|
||||
if not svl.with_company(svl.company_id).product_id.valuation == 'real_time':
|
||||
continue
|
||||
if svl.currency_id.is_zero(svl.value):
|
||||
continue
|
||||
svl.stock_move_id._account_entry_move(svl.quantity, svl.description, svl.id, svl.value)
|
||||
svl.stock_move_id.with_company(svl.company_id)._account_entry_move(svl.quantity, svl.description, svl.id, svl.value)
|
||||
|
||||
stock_valuation_layers._check_company()
|
||||
|
||||
|
||||
@@ -97,11 +97,22 @@ function _genericJsonRpc (fct_name, params, settings, fct) {
|
||||
});
|
||||
|
||||
// FIXME: jsonp?
|
||||
promise.abort = function () {
|
||||
rejection({
|
||||
message: "XmlHttpRequestError abort",
|
||||
event: $.Event('abort')
|
||||
});
|
||||
/**
|
||||
* @param {Boolean} rejectError Returns an error if true. Allows you to cancel
|
||||
* ignored rpc's in order to unblock the ui and not display an error.
|
||||
*/
|
||||
promise.abort = function (rejectError = true) {
|
||||
if (rejectError) {
|
||||
rejection({
|
||||
message: "XmlHttpRequestError abort",
|
||||
event: $.Event('abort')
|
||||
});
|
||||
}
|
||||
|
||||
if (!shadow) {
|
||||
core.bus.trigger('rpc_response');
|
||||
}
|
||||
|
||||
if (xhr.abort) {
|
||||
xhr.abort();
|
||||
}
|
||||
|
||||
@@ -576,7 +576,10 @@ var FieldMany2One = AbstractField.extend({
|
||||
domain.push(['id', 'not in', blackListedIds]);
|
||||
}
|
||||
|
||||
const nameSearch = this._rpc({
|
||||
if (this.lastNameSearch) {
|
||||
this.lastNameSearch.abort(false)
|
||||
}
|
||||
this.lastNameSearch = this._rpc({
|
||||
model: this.field.relation,
|
||||
method: "name_search",
|
||||
kwargs: {
|
||||
@@ -587,7 +590,7 @@ var FieldMany2One = AbstractField.extend({
|
||||
context,
|
||||
}
|
||||
});
|
||||
const results = await this.orderer.add(nameSearch);
|
||||
const results = await this.orderer.add(this.lastNameSearch);
|
||||
|
||||
// Format results to fit the options dropdown
|
||||
let values = results.map((result) => {
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
}
|
||||
}
|
||||
.calendar-time {
|
||||
/*rtl:ignore*/
|
||||
direction:ltr;
|
||||
select {
|
||||
&.hourselect, &.minuteselect, &.secondselect, &.ampmselect {
|
||||
display: initial;
|
||||
|
||||
@@ -151,8 +151,10 @@ var MockServer = Class.extend({
|
||||
if (abort) {
|
||||
abort = abort.bind(def);
|
||||
} else {
|
||||
abort = function () {
|
||||
throw new Error("Can't abort this request");
|
||||
abort = function (rejectError = true) {
|
||||
if (rejectError) {
|
||||
throw new ConnectionAbortedError("XmlHttpRequestError abort");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user