Initial commit: Odoo 18.0-20251222 extra-addons
This commit is contained in:
153
account_cash_deposit/models/cash_unit.py
Executable file
153
account_cash_deposit/models/cash_unit.py
Executable file
@@ -0,0 +1,153 @@
|
||||
# Copyright 2022 Akretion France (http://www.akretion.com/)
|
||||
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
import logging
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools.misc import format_amount
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CashUnit(models.Model):
|
||||
_name = "cash.unit"
|
||||
_description = "Cash Unit"
|
||||
_order = "currency_id, tree_order desc"
|
||||
_rec_name = "value"
|
||||
|
||||
# In the point_of_sale module, there is a model pos.bill that represent coins
|
||||
# and notes. But we can't use it because it's in the point_of_sale module and
|
||||
# the datamodel is very badly designed. That's why we create our own model.
|
||||
|
||||
currency_id = fields.Many2one("res.currency", ondelete="cascade")
|
||||
active = fields.Boolean(default=True)
|
||||
tree_order = fields.Float(compute="_compute_all", store=True)
|
||||
cash_type = fields.Selection(
|
||||
[
|
||||
("note", "Note"),
|
||||
("coin", "Coin"),
|
||||
("coinroll", "Coin Roll"),
|
||||
],
|
||||
string="Type",
|
||||
required=True,
|
||||
help="This field should never be modified.",
|
||||
)
|
||||
coinroll_qty = fields.Integer(
|
||||
string="Coin Quantity", help="This field should never be modified."
|
||||
)
|
||||
value = fields.Monetary(
|
||||
required=True,
|
||||
help="This field should never be modified. For a coin roll, "
|
||||
"you must enter the value of the coin.",
|
||||
)
|
||||
total_value = fields.Monetary(compute="_compute_all", store=True)
|
||||
auto_create = fields.Selection(
|
||||
[
|
||||
("deposit", "Deposit"),
|
||||
("order", "Order"),
|
||||
("both", "Both"),
|
||||
],
|
||||
help="If set, a line for this cash unit will be created by default "
|
||||
"on a new cash deposit or a new cash order or both.",
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
(
|
||||
"coinroll_qty_positive",
|
||||
"CHECK(coinroll_qty >= 0)",
|
||||
"The coin quantity must be positive.",
|
||||
),
|
||||
("value_positive", "CHECK(value > 0)", "The value must be strictly positive."),
|
||||
]
|
||||
|
||||
@api.constrains("cash_type", "coinroll_qty")
|
||||
def _check_cash_unit(self):
|
||||
for rec in self:
|
||||
if rec.cash_type == "coinroll" and rec.coinroll_qty <= 0:
|
||||
raise ValidationError(
|
||||
_("For a coin roll, the coin quantity must be strictly positive.")
|
||||
)
|
||||
|
||||
@api.depends("coinroll_qty", "cash_type", "value")
|
||||
def _compute_all(self):
|
||||
# I want bank notes first, then coinrolls, then coins
|
||||
# This is a hack, but it is designed to work fine with
|
||||
# all currencies of the planet !
|
||||
type2multiplier = {
|
||||
"note": 1000000,
|
||||
"coinroll": 1000,
|
||||
"coin": 1,
|
||||
}
|
||||
for rec in self:
|
||||
qty = rec.cash_type == "coinroll" and rec.coinroll_qty or 1
|
||||
total_value = rec.value * qty
|
||||
rec.tree_order = type2multiplier.get(rec.cash_type, 0) * total_value
|
||||
rec.total_value = total_value
|
||||
|
||||
def _get_value_label(self, value):
|
||||
self.ensure_one()
|
||||
symbol_position = self.currency_id.position
|
||||
symbol = self.currency_id.symbol
|
||||
int_value = int(round(value))
|
||||
# if value is an integer
|
||||
if self.currency_id.compare_amounts(value, int_value) == 0:
|
||||
amount_label = str(int_value)
|
||||
if symbol_position == "before":
|
||||
value_label = f"{symbol} {amount_label}"
|
||||
else:
|
||||
value_label = f"{amount_label} {symbol}"
|
||||
else:
|
||||
value_label = format_amount(self.env, value, self.currency_id)
|
||||
return value_label
|
||||
|
||||
@api.depends("cash_type", "value", "coinroll_qty")
|
||||
def _compute_display_name(self):
|
||||
type2label = dict(
|
||||
self.fields_get("cash_type", "selection")["cash_type"]["selection"]
|
||||
)
|
||||
for rec in self:
|
||||
cash_type_label = type2label.get(rec.cash_type)
|
||||
value_label = rec._get_value_label(rec.value)
|
||||
if rec.cash_type == "coinroll":
|
||||
total_value_label = rec._get_value_label(rec.total_value)
|
||||
label = (
|
||||
f"{cash_type_label} {value_label} x {rec.coinroll_qty} "
|
||||
f"({total_value_label})"
|
||||
)
|
||||
else:
|
||||
label = f"{cash_type_label} {value_label}"
|
||||
rec.display_name = label
|
||||
|
||||
@api.model
|
||||
def name_search(self, name="", args=None, operator="ilike", limit=100):
|
||||
if args is None:
|
||||
args = []
|
||||
if name and operator == "ilike":
|
||||
if name.isdigit():
|
||||
recs = self.search([("value", "=", name)] + args, limit=limit)
|
||||
if recs:
|
||||
return [(rec.id, rec.display_name) for rec in recs]
|
||||
value = False
|
||||
try:
|
||||
value = float(name)
|
||||
except ValueError:
|
||||
logger.debug("name %s is not a float. Make pylint happy.", name)
|
||||
if value:
|
||||
recs = self.search([("value", "=", value)] + args, limit=limit)
|
||||
if recs:
|
||||
return [(rec.id, rec.display_name) for rec in recs]
|
||||
lang = self.env["res.lang"]._lang_get(self.env.user.lang)
|
||||
if lang:
|
||||
decimal_sep = lang.decimal_point
|
||||
if decimal_sep and decimal_sep != ".":
|
||||
try:
|
||||
value = float(name.replace(decimal_sep, ".", 1))
|
||||
except ValueError:
|
||||
logger.debug("name %s is not a float. Make pylint happy.", name)
|
||||
if value:
|
||||
recs = self.search([("value", "=", value)] + args, limit=limit)
|
||||
if recs:
|
||||
return [(rec.id, rec.display_name) for rec in recs]
|
||||
return super().name_search(name=name, args=args, operator=operator, limit=limit)
|
||||
Reference in New Issue
Block a user