You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

125 lines
5.4 KiB

# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Anfas Faisal K (odoo@cybrosys.info)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
from odoo import api, models, fields, _
from odoo.exceptions import UserError
from odoo.osv.expression import AND
from odoo.tools import float_round, float_is_zero
class PosOrder(models.Model):
"""
This class extends the 'pos.order' model to introduce additional
functionality related to partial payments and order management in the
Point of Sale (POS) system.
It adds fields and methods for tracking partial payments, computing due
amounts, and marking orders as paid. The class also includes a method to
search for partial orders based on specified criteria.
"""
_inherit = 'pos.order'
is_partial_payment = fields.Boolean(string="Is Partial Payment",
default=False,
help="Flag indicating whether this POS "
"order is a partial payment.")
due_amount = fields.Float(string="Amount Due",
compute='_compute_due_amount',
store=True,
help="The amount remaining to be paid for this"
"POS order.")
@api.depends('amount_total', 'amount_paid')
def _compute_due_amount(self):
"""
Compute the due amount for the POS order.
This method computes the difference between the total amount and the amount paid
for the POS order and updates the 'due_amount' field accordingly.
"""
for record in self:
record.due_amount = record.amount_total - record.amount_paid
def _order_fields(self, ui_order):
"""
Prepare dictionary for create method
This method prepares a dictionary of order fields for creating a POS order based
on the data from the user interface (UI) order.
"""
result = super()._order_fields(ui_order)
result['is_partial_payment'] = ui_order.get('is_partial_payment')
return result
def action_pos_order_paid(self):
"""
Mark the POS order as paid. This method marks the POS order as
paid and ensures that it is fully paid based on the partial
payment.
"""
self.ensure_one()
# TODO: add support for mix of cash and non-cash payments when both cash_rounding and only_round_cash_method are True
if not self.config_id.cash_rounding \
or self.config_id.only_round_cash_method \
and not any(
p.payment_method_id.is_cash_count for p in self.payment_ids):
total = self.amount_total
else:
total = float_round(self.amount_total,
precision_rounding=self.config_id.rounding_method.rounding,
rounding_method=self.config_id.rounding_method.rounding_method)
isPaid = float_is_zero(total - self.amount_paid,
precision_rounding=self.currency_id.rounding)
if not isPaid:
pos_config = self.env['pos.config'].search([])
for shop in pos_config:
if shop.partial_payment:
isPaid = True
if not isPaid and not self.config_id.cash_rounding:
raise UserError(_("Order %s is not fully paid.", self.name))
elif not isPaid and self.config_id.cash_rounding:
currency = self.currency_id
if self.config_id.rounding_method.rounding_method == "HALF-UP":
maxDiff = currency.round(
self.config_id.rounding_method.rounding / 2)
else:
maxDiff = currency.round(
self.config_id.rounding_method.rounding)
diff = currency.round(self.amount_total - self.amount_paid)
if not abs(diff) <= maxDiff:
raise UserError(_("Order %s is not fully paid.", self.name))
self.write({'state': 'paid'})
return True
@api.model
def search_partial_order_ids(self, config_id, domain, limit, offset):
"""Search for 'partial' orders that satisfy the given domain,
limit and offset."""
default_domain = ['&', ('config_id', '=', config_id),
('is_partial_payment', '=', True), '!', '|',
('state', '=', 'draft'), ('state', '=', 'cancelled')]
real_domain = AND([domain, default_domain])
ids = self.search(AND([domain, default_domain]), limit=limit,
offset=offset).ids
totalCount = self.search_count(real_domain)
return {'ids': ids, 'totalCount': totalCount}