mirror of
https://gitlab.com/flectra-hq/flectra.git
synced 2025-02-25 18:55:21 -06:00
[PATCH] Upstream patch - 15012023
This commit is contained in:
@@ -564,7 +564,7 @@ class AccountReconcileModel(models.Model):
|
||||
or (self.match_amount == 'between' and (abs(st_line.amount) > self.match_amount_max or abs(st_line.amount) < self.match_amount_min))
|
||||
or (self.match_partner and not partner)
|
||||
or (self.match_partner and self.match_partner_ids and partner not in self.match_partner_ids)
|
||||
or (self.match_partner and self.match_partner_category_ids and partner.category_id not in self.match_partner_category_ids)
|
||||
or (self.match_partner and self.match_partner_category_ids and not (partner.category_id & self.match_partner_category_ids))
|
||||
):
|
||||
return False
|
||||
|
||||
|
||||
@@ -455,7 +455,9 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
|
||||
|
||||
def test_matching_fields_match_partner_category_ids(self):
|
||||
test_category = self.env['res.partner.category'].create({'name': 'Consulting Services'})
|
||||
self.partner_2.category_id = test_category
|
||||
test_category2 = self.env['res.partner.category'].create({'name': 'Consulting Services2'})
|
||||
|
||||
self.partner_2.category_id = test_category + test_category2
|
||||
self.rule_1.match_partner_category_ids |= test_category
|
||||
self._check_statement_matching(self.rule_1, {
|
||||
self.bank_line_1.id: {'aml_ids': []},
|
||||
|
||||
@@ -116,5 +116,7 @@ class User(models.Model):
|
||||
_logger.info("Calendar Synchro - Starting synchronization for %s", user)
|
||||
try:
|
||||
user.with_user(user).sudo()._sync_google_calendar(google)
|
||||
self.env.cr.commit()
|
||||
except Exception as e:
|
||||
_logger.exception("[%s] Calendar Synchro - Exception : %s !", user, exception_to_unicode(e))
|
||||
self.env.cr.rollback()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<flectra>
|
||||
|
||||
<record id="view_move_form_inherit_l10n_cl" model="ir.ui.view">
|
||||
<field name="name">account.move.form.inherit.l10n.cl</field>
|
||||
<field name="model">account.move</field>
|
||||
@@ -9,9 +8,6 @@
|
||||
<form>
|
||||
<field name="l10n_latam_internal_type" invisible="1"/>
|
||||
</form>
|
||||
<field name="ref" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('move_type', '=', 'out_invoice'), ('l10n_latam_internal_type', '!=', 'debit_note')], 'required': [('l10n_latam_internal_type', '=', 'debit_note')]}</attribute>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -2,11 +2,51 @@
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from flectra import models
|
||||
from flectra.tools import float_compare
|
||||
|
||||
|
||||
class StockPicking(models.Model):
|
||||
_inherit = 'stock.picking'
|
||||
|
||||
def _action_done(self):
|
||||
""" If needed, create a compensation layer, so we add the MO cost
|
||||
to the dropship one
|
||||
"""
|
||||
res = super()._action_done()
|
||||
for move in self.move_lines:
|
||||
if not (move.is_subcontract and move._is_dropshipped() and move.state == 'done'):
|
||||
continue
|
||||
|
||||
dropship_svls = move.stock_valuation_layer_ids
|
||||
if not dropship_svls:
|
||||
continue
|
||||
|
||||
subcontract_svls = move.move_orig_ids.stock_valuation_layer_ids
|
||||
subcontract_value = sum(subcontract_svls.mapped('value'))
|
||||
dropship_value = abs(sum(dropship_svls.mapped('value')))
|
||||
diff = subcontract_value - dropship_value
|
||||
if float_compare(diff, 0, precision_rounding=move.company_id.currency_id.rounding) <= 0:
|
||||
continue
|
||||
|
||||
svl_vals = move._prepare_common_svl_vals()
|
||||
svl_vals.update({
|
||||
'remaining_value': 0,
|
||||
'remaining_qty': 0,
|
||||
'value': -diff,
|
||||
'quantity': 0,
|
||||
'unit_cost': 0,
|
||||
'stock_valuation_layer_id': dropship_svls[0].id,
|
||||
'stock_move_id': False,
|
||||
})
|
||||
svl = self.env['stock.valuation.layer'].create(svl_vals)
|
||||
|
||||
move = move.with_company(move.company_id)
|
||||
if move.product_id.valuation != 'real_time':
|
||||
continue
|
||||
move._account_entry_move(svl.quantity, svl.description, svl.id, svl.value)
|
||||
|
||||
return res
|
||||
|
||||
def _get_warehouse(self, subcontract_move):
|
||||
if subcontract_move.sale_line_id:
|
||||
return subcontract_move.sale_line_id.order_id.warehouse_id
|
||||
|
||||
@@ -3,3 +3,4 @@
|
||||
|
||||
from . import test_purchase_subcontracting
|
||||
from . import test_sale_dropshipping
|
||||
from . import test_anglo_saxon_valuation
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from flectra.addons.stock_account.tests.test_anglo_saxon_valuation_reconciliation_common import ValuationReconciliationTestCommon
|
||||
from flectra.tests import tagged, Form
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestSubcontractingDropshippingValuation(ValuationReconciliationTestCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls, chart_template_ref=None):
|
||||
super().setUpClass(chart_template_ref=chart_template_ref)
|
||||
|
||||
categ_form = Form(cls.env['product.category'])
|
||||
categ_form.name = 'fifo auto'
|
||||
categ_form.parent_id = cls.env.ref('product.product_category_all')
|
||||
categ_form.property_cost_method = 'fifo'
|
||||
categ_form.property_valuation = 'real_time'
|
||||
cls.categ_fifo_auto = categ_form.save()
|
||||
|
||||
(cls.product_a | cls.product_b).type = 'product'
|
||||
|
||||
cls.bom_a = cls.env['mrp.bom'].create({
|
||||
'product_tmpl_id': cls.product_a.product_tmpl_id.id,
|
||||
'type': 'subcontract',
|
||||
'subcontractor_ids': [(6, 0, cls.partner_a.ids)],
|
||||
'bom_line_ids': [
|
||||
(0, 0, {'product_id': cls.product_b.id, 'product_qty': 1.0}),
|
||||
],
|
||||
})
|
||||
|
||||
def test_valuation_subcontracted_and_dropshipped(self):
|
||||
"""
|
||||
Product:
|
||||
- FIFO + Auto
|
||||
- Subcontracted
|
||||
Purchase 2 from Subcontractor to a customer (dropship).
|
||||
Then return 1 to subcontractor and one to stock
|
||||
It should generate the correct valuations AMLs
|
||||
"""
|
||||
# pylint: disable=bad-whitespace
|
||||
all_amls_ids = self.env['account.move.line'].search_read([], ['id'])
|
||||
|
||||
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
|
||||
self.env.user.write({'groups_id': [(4, grp_multi_loc.id)]})
|
||||
|
||||
(self.product_a | self.product_b).categ_id = self.categ_fifo_auto
|
||||
self.product_b.standard_price = 10
|
||||
|
||||
dropship_picking_type = self.env['stock.picking.type'].search([
|
||||
('company_id', '=', self.env.company.id),
|
||||
('default_location_src_id.usage', '=', 'supplier'),
|
||||
('default_location_dest_id.usage', '=', 'customer'),
|
||||
], limit=1, order='sequence')
|
||||
|
||||
po = self.env['purchase.order'].create({
|
||||
"partner_id": self.partner_a.id,
|
||||
"picking_type_id": dropship_picking_type.id,
|
||||
"dest_address_id": self.partner_b.id,
|
||||
"order_line": [(0, 0, {
|
||||
'product_id': self.product_a.id,
|
||||
'name': self.product_a.name,
|
||||
'product_qty': 2.0,
|
||||
'price_unit': 100,
|
||||
'taxes_id': False,
|
||||
})],
|
||||
})
|
||||
po.button_confirm()
|
||||
|
||||
delivery = po.picking_ids
|
||||
res = delivery.button_validate()
|
||||
Form(self.env['stock.immediate.transfer'].with_context(res['context'])).save().process()
|
||||
|
||||
stock_in_acc_id = self.categ_fifo_auto.property_stock_account_input_categ_id.id
|
||||
stock_out_acc_id = self.categ_fifo_auto.property_stock_account_output_categ_id.id
|
||||
stock_valu_acc_id = self.categ_fifo_auto.property_stock_valuation_account_id.id
|
||||
|
||||
amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)])
|
||||
all_amls_ids += amls.ids
|
||||
self.assertRecordValues(amls, [
|
||||
# Compensation of dropshipping value
|
||||
{'account_id': stock_valu_acc_id, 'product_id': self.product_a.id, 'debit': 0.0, 'credit': 20.0},
|
||||
{'account_id': stock_out_acc_id, 'product_id': self.product_a.id, 'debit': 20.0, 'credit': 0.0},
|
||||
# Receipt from subcontractor
|
||||
{'account_id': stock_in_acc_id, 'product_id': self.product_a.id, 'debit': 0.0, 'credit': 220.0},
|
||||
{'account_id': stock_valu_acc_id, 'product_id': self.product_a.id, 'debit': 220.0, 'credit': 0.0},
|
||||
# Delivery to subcontractor
|
||||
{'account_id': stock_valu_acc_id, 'product_id': self.product_b.id, 'debit': 0.0, 'credit': 20.0},
|
||||
{'account_id': stock_out_acc_id, 'product_id': self.product_b.id, 'debit': 20.0, 'credit': 0.0},
|
||||
# Initial dropshipped value
|
||||
{'account_id': stock_valu_acc_id, 'product_id': self.product_a.id, 'debit': 0.0, 'credit': 200.0},
|
||||
{'account_id': stock_out_acc_id, 'product_id': self.product_a.id, 'debit': 200.0, 'credit': 0.0},
|
||||
])
|
||||
|
||||
# return to subcontracting location
|
||||
sbc_location = self.env.company.subcontracting_location_id
|
||||
return_form = Form(self.env['stock.return.picking'].with_context(active_id=delivery.id, active_model='stock.picking'))
|
||||
return_form.location_id = sbc_location
|
||||
with return_form.product_return_moves.edit(0) as line:
|
||||
line.quantity = 1
|
||||
return_wizard = return_form.save()
|
||||
return_id, _ = return_wizard._create_returns()
|
||||
return_picking = self.env['stock.picking'].browse(return_id)
|
||||
return_picking.move_lines.quantity_done = 1
|
||||
return_picking.button_validate()
|
||||
|
||||
amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)])
|
||||
all_amls_ids += amls.ids
|
||||
self.assertRecordValues(amls, [
|
||||
{'account_id': stock_valu_acc_id, 'product_id': self.product_a.id, 'debit': 0.0, 'credit': 110.0},
|
||||
{'account_id': stock_in_acc_id, 'product_id': self.product_a.id, 'debit': 110.0, 'credit': 0.0},
|
||||
])
|
||||
|
||||
# return to stock location
|
||||
warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
|
||||
stock_location = warehouse.lot_stock_id
|
||||
stock_location.return_location = True
|
||||
return_form = Form(self.env['stock.return.picking'].with_context(active_id=delivery.id, active_model='stock.picking'))
|
||||
return_form.location_id = stock_location
|
||||
with return_form.product_return_moves.edit(0) as line:
|
||||
line.quantity = 1
|
||||
return_wizard = return_form.save()
|
||||
return_id, _ = return_wizard._create_returns()
|
||||
return_picking = self.env['stock.picking'].browse(return_id)
|
||||
return_picking.move_lines.quantity_done = 1
|
||||
return_picking.button_validate()
|
||||
|
||||
amls = self.env['account.move.line'].search([('id', 'not in', all_amls_ids)])
|
||||
all_amls_ids += amls.ids
|
||||
self.assertRecordValues(amls, [
|
||||
{'account_id': stock_out_acc_id, 'product_id': self.product_a.id, 'debit': 0.0, 'credit': 110.0},
|
||||
{'account_id': stock_valu_acc_id, 'product_id': self.product_a.id, 'debit': 110.0, 'credit': 0.0},
|
||||
])
|
||||
@@ -6,14 +6,6 @@
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.oe_form_gantt_avatars:after {
|
||||
font-family: "mnmliconsRegular" !important;
|
||||
font-size: 21px;
|
||||
font-weight: 300 !important;
|
||||
content: "y";
|
||||
top: 3px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.openerp .oe_kanban_view .oe_kanban_project {
|
||||
width: 250px;
|
||||
|
||||
@@ -37,7 +37,7 @@ class StockMove(models.Model):
|
||||
def _get_price_unit(self):
|
||||
""" Returns the unit price for the move"""
|
||||
self.ensure_one()
|
||||
if self.purchase_line_id and self.product_id.id == self.purchase_line_id.product_id.id:
|
||||
if not self.origin_returned_move_id and self.purchase_line_id and self.product_id.id == self.purchase_line_id.product_id.id:
|
||||
price_unit_prec = self.env['decimal.precision'].precision_get('Product Price')
|
||||
line = self.purchase_line_id
|
||||
order = line.order_id
|
||||
|
||||
@@ -555,6 +555,8 @@ class Product(models.Model):
|
||||
'route_ids': route_ids,
|
||||
'warehouse_id': location.get_warehouse()
|
||||
})
|
||||
if rule in seen_rules:
|
||||
raise UserError(_("Invalid rule's configuration, the following rule causes an endless loop: %s", rule.display_name))
|
||||
if not rule:
|
||||
return seen_rules
|
||||
if rule.procure_method == 'make_to_stock' or rule.action not in ('pull_push', 'pull'):
|
||||
|
||||
@@ -5,6 +5,7 @@ from datetime import date, datetime, timedelta
|
||||
|
||||
from flectra.tests.common import Form, TransactionCase
|
||||
from flectra.tools import mute_logger
|
||||
from flectra.exceptions import UserError
|
||||
|
||||
|
||||
class TestProcRule(TransactionCase):
|
||||
@@ -19,6 +20,40 @@ class TestProcRule(TransactionCase):
|
||||
})
|
||||
self.partner = self.env['res.partner'].create({'name': 'Partner'})
|
||||
|
||||
def test_endless_loop_rules_from_location(self):
|
||||
""" Creates and configure a rule the way, when trying to get rules from
|
||||
location, it goes in a state where the found rule tries to trigger another
|
||||
rule but finds nothing else than itself and so get stuck in a recursion error."""
|
||||
warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
|
||||
reception_route = warehouse.reception_route_id
|
||||
self.product.type = 'product'
|
||||
|
||||
# Creates a delivery for this product, that way, this product will be to resupply.
|
||||
picking_form = Form(self.env['stock.picking'])
|
||||
picking_form.picking_type_id = warehouse.out_type_id
|
||||
with picking_form.move_ids_without_package.new() as move_line:
|
||||
move_line.product_id = self.product
|
||||
move_line.product_uom_qty = 10
|
||||
delivery = picking_form.save()
|
||||
delivery.action_confirm()
|
||||
self.product._compute_quantities() # Computes `outgoing_qty` to have the orderpoint.
|
||||
|
||||
# Then, creates a rule and adds it into the route's rules.
|
||||
reception_route.rule_ids.action_archive()
|
||||
self.env['stock.rule'].create({
|
||||
'name': 'Looping Rule',
|
||||
'route_id': reception_route.id,
|
||||
'location_id': warehouse.lot_stock_id.id,
|
||||
'location_src_id': warehouse.lot_stock_id.id,
|
||||
'action': 'pull_push',
|
||||
'procure_method': 'make_to_order',
|
||||
'picking_type_id': warehouse.int_type_id.id,
|
||||
})
|
||||
|
||||
# Tries to open the Replenishment view -> It should raise an UserError.
|
||||
with self.assertRaises(UserError):
|
||||
self.env['stock.warehouse.orderpoint'].action_open_orderpoints()
|
||||
|
||||
def test_proc_rule(self):
|
||||
# Create a product route containing a stock rule that will
|
||||
# generate a move from Stock for every procurement created in Output
|
||||
|
||||
@@ -40,6 +40,7 @@ class StockMove(models.Model):
|
||||
# If the move is a return, use the original move's price unit.
|
||||
if self.origin_returned_move_id and self.origin_returned_move_id.sudo().stock_valuation_layer_ids:
|
||||
layers = self.origin_returned_move_id.sudo().stock_valuation_layer_ids
|
||||
layers |= layers.stock_valuation_layer_ids
|
||||
quantity = sum(layers.mapped("quantity"))
|
||||
return layers.currency_id.round(sum(layers.mapped("value")) / quantity) if not float_is_zero(quantity, precision_rounding=layers.uom_id.rounding) else 0
|
||||
return price_unit if not float_is_zero(price_unit, precision) or self._should_force_price_unit() else self.product_id.standard_price
|
||||
@@ -80,7 +81,7 @@ class StockMove(models.Model):
|
||||
:rtype: bool
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self._get_in_move_lines():
|
||||
if self._get_in_move_lines() and not self._is_dropshipped_returned():
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -108,7 +109,7 @@ class StockMove(models.Model):
|
||||
:rtype: bool
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self._get_out_move_lines():
|
||||
if self._get_out_move_lines() and not self._is_dropshipped():
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -214,23 +215,26 @@ class StockMove(models.Model):
|
||||
|
||||
common_vals = dict(move._prepare_common_svl_vals(), remaining_qty=0)
|
||||
|
||||
# create the in
|
||||
in_vals = {
|
||||
'unit_cost': unit_cost,
|
||||
'value': unit_cost * quantity,
|
||||
'quantity': quantity,
|
||||
}
|
||||
in_vals.update(common_vals)
|
||||
svl_vals_list.append(in_vals)
|
||||
# create the in if it does not come from a valued location (eg subcontract -> customer)
|
||||
if not move.location_id._should_be_valued():
|
||||
in_vals = {
|
||||
'unit_cost': unit_cost,
|
||||
'value': unit_cost * quantity,
|
||||
'quantity': quantity,
|
||||
}
|
||||
in_vals.update(common_vals)
|
||||
svl_vals_list.append(in_vals)
|
||||
|
||||
# create the out if it does not go to a valued location (eg customer -> subcontract)
|
||||
if not move.location_dest_id._should_be_valued():
|
||||
out_vals = {
|
||||
'unit_cost': unit_cost,
|
||||
'value': unit_cost * quantity * -1,
|
||||
'quantity': quantity * -1,
|
||||
}
|
||||
out_vals.update(common_vals)
|
||||
svl_vals_list.append(out_vals)
|
||||
|
||||
# create the out
|
||||
out_vals = {
|
||||
'unit_cost': unit_cost,
|
||||
'value': unit_cost * quantity * -1,
|
||||
'quantity': quantity * -1,
|
||||
}
|
||||
out_vals.update(common_vals)
|
||||
svl_vals_list.append(out_vals)
|
||||
return self.env['stock.valuation.layer'].sudo().create(svl_vals_list)
|
||||
|
||||
def _create_dropshipped_returned_svl(self, forced_quantity=None):
|
||||
|
||||
93
addons/web/static/src/fonts/sign/OoohBaby-Regular-ofl.txt
Normal file
93
addons/web/static/src/fonts/sign/OoohBaby-Regular-ofl.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2004-2021 The Oooh Baby Project Authors (https://github.com/googlefonts/oooh-baby)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
BIN
addons/web/static/src/fonts/sign/OoohBaby-Regular.ttf
Normal file
BIN
addons/web/static/src/fonts/sign/OoohBaby-Regular.ttf
Normal file
Binary file not shown.
@@ -11,6 +11,15 @@ from functools import partial
|
||||
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
# Cleanup records which are related to websites and will not be autocleaned
|
||||
# by the uninstall operation. This must be done here in the uninstall_hook
|
||||
# as during an uninstallation, `unlink` is not called for records which were
|
||||
# created by the user (not XML data). Same goes for @api.ondelete available
|
||||
# from 15.0 and above.
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
env['website'].search([])._remove_attachments_on_website_unlink()
|
||||
|
||||
# Properly unlink website_id from ir.model.fields
|
||||
def rem_website_id_null(dbname):
|
||||
db_registry = flectra.modules.registry.Registry.new(dbname)
|
||||
with api.Environment.manage(), db_registry.cursor() as cr:
|
||||
|
||||
@@ -135,6 +135,15 @@ class Assets(models.AbstractModel):
|
||||
self = self.sudo()
|
||||
website = self.env['website'].get_current_website()
|
||||
res = super(Assets, self)._get_custom_attachment(custom_url, op=op)
|
||||
# FIXME (?) In website, those attachments should always have been
|
||||
# created with a website_id. The "not website_id" part in the following
|
||||
# condition might therefore be useless (especially since the attachments
|
||||
# do not seem ordered). It was developed in the spirit of served
|
||||
# attachments which follow this rule of "serve what belongs to the
|
||||
# current website or all the websites" but it probably does not make
|
||||
# sense here. It however allowed to discover a bug where attachments
|
||||
# were left without website_id. This will be kept untouched in stable
|
||||
# but will be reviewed and made more robust in master.
|
||||
return res.with_context(website_id=website.id).filtered(lambda x: not x.website_id or x.website_id == website)
|
||||
|
||||
def _get_custom_view(self, custom_url, op='='):
|
||||
|
||||
@@ -244,6 +244,11 @@ class Website(models.Model):
|
||||
if not website:
|
||||
raise UserError(_('You must keep at least one website.'))
|
||||
|
||||
self._remove_attachments_on_website_unlink()
|
||||
|
||||
return super().unlink()
|
||||
|
||||
def _remove_attachments_on_website_unlink(self):
|
||||
# Do not delete invoices, delete what's strictly necessary
|
||||
attachments_to_unlink = self.env['ir.attachment'].search([
|
||||
('website_id', 'in', self.ids),
|
||||
@@ -253,7 +258,6 @@ class Website(models.Model):
|
||||
('url', 'ilike', '.assets\\_'),
|
||||
])
|
||||
attachments_to_unlink.unlink()
|
||||
return super(Website, self).unlink()
|
||||
|
||||
def create_and_redirect_to_theme(self):
|
||||
self._force()
|
||||
|
||||
Reference in New Issue
Block a user