NINS_CODE/bahmni-addons/bahmni_account/models/account_invoice.py
travelershot 70dda814aa codepush
2024-12-12 22:37:39 +06:00

287 lines
14 KiB
Python

# -*- coding: utf-8 -*-
from odoo import fields, models, api, _
from odoo.exceptions import UserError, ValidationError
import logging
_logger = logging.getLogger(__name__)
# mapping invoice type to refund type
TYPE2REFUND = {
'out_invoice': 'out_refund', # Customer Invoice
'in_invoice': 'in_refund', # Vendor Bill
'out_refund': 'out_invoice', # Customer Refund
'in_refund': 'in_invoice', # Vendor Refund
}
class AccountInvoice(models.Model):
_inherit = 'account.invoice'
# # overridden this method to deduct discounted amount from total of invoice
@api.one
@api.depends('invoice_line_ids.price_subtotal', 'tax_line_ids.amount',
'currency_id', 'company_id', 'date_invoice', 'type', 'discount')
def _compute_amount(self):
round_curr = self.currency_id.round
self.amount_untaxed = sum(line.price_subtotal for line in self.invoice_line_ids)
self.amount_tax = sum(round_curr(line.amount) for line in self.tax_line_ids)
amount_total = self.amount_untaxed + self.amount_tax - self.discount
self.round_off_amount = self.env['rounding.off'].round_off_value_to_nearest(amount_total)
self.amount_total = self.amount_untaxed + self.amount_tax - self.discount + self.round_off_amount
amount_total_company_signed = self.amount_total
amount_untaxed_signed = self.amount_untaxed
if self.currency_id and self.company_id and self.currency_id != self.company_id.currency_id:
currency_id = self.currency_id.with_context(date=self.date_invoice)
amount_total_company_signed = currency_id.compute(self.amount_total, self.company_id.currency_id)
amount_untaxed_signed = currency_id.compute(self.amount_untaxed, self.company_id.currency_id)
sign = self.type in ['in_refund', 'out_refund'] and -1 or 1
self.amount_total_company_signed = amount_total_company_signed * sign
self.amount_total_signed = self.amount_total * sign
self.amount_untaxed_signed = amount_untaxed_signed * sign
discount_type = fields.Selection([('none', 'No Discount'),
('fixed', 'Fixed'),
('percentage', 'Percentage')],
string="Discount Method",
default='none')
discount = fields.Monetary(string="Discount")
discount_percentage = fields.Float(string="Discount Percentage")
disc_acc_id = fields.Many2one('account.account',
string="Discount Account Head")
round_off_amount = fields.Monetary(string="Round Off Amount",
compute=_compute_amount)
@api.onchange('invoice_line_ids')
def onchange_invoice_lines(self):
amount_total = self.amount_untaxed + self.amount_tax
if self.discount_type == 'fixed':
self.discount_percentage = (self.discount / amount_total) * 100
elif self.discount_type == 'percentage':
self.discount = amount_total * self.discount_percentage / 100
@api.onchange('discount', 'discount_percentage', 'discount_type')
def onchange_discount(self):
amount_total = self.amount_untaxed + self.amount_tax
if self.discount:
self.discount_percentage = (self.discount / amount_total) * 100
if self.discount_percentage:
self.discount = amount_total * self.discount_percentage / 100
@api.multi
def _find_batch(self, product, qty, location, picking):
_logger.info("\n\n***** Product :%s, Quantity :%s Location :%s\n*****",product,qty,location)
lot_objs = self.env['stock.production.lot'].search([('product_id','=',product.id),('life_date','>=',str(fields.datetime.now()))])
_logger.info('\n *** Searched Lot Objects:%s \n',lot_objs)
if any(lot_objs):
#Sort losts based on the expiry date FEFO(First Expiry First Out)
lot_objs = list(lot_objs)
sorted_lot_list = sorted(lot_objs, key=lambda l: l.life_date)
_logger.info('\n *** Sorted based on FEFO :%s \n',sorted_lot_list)
done_qty = qty
res_lot_ids = []
lot_ids_for_query = tuple([lot.id for lot in sorted_lot_list])
self._cr.execute("SELECT SUM(qty) FROM stock_quant WHERE lot_id IN %s and location_id=%s",(lot_ids_for_query,location.id,))
qry_rslt = self._cr.fetchall()
available_qty = qry_rslt[0] and qry_rslt[0][0] or 0
if available_qty >= qty:
for lot_obj in sorted_lot_list:
quants = lot_obj.quant_ids.filtered(lambda q: q.location_id == location)
for quant in quants:
if done_qty >= 0:
res_lot_ids.append(lot_obj)
done_qty = done_qty - quant.qty
return res_lot_ids
else:
message = ("<b>Auto validation Failed</b> <br/> <b>Reason:</b> There are not enough stock available for <a href=# data-oe-model=product.product data-oe-id=%d>%s</a> product on <a href=# data-oe-model=stock.location data-oe-id=%d>%s</a> Location") % (product.id,product.name,location.id,location.name)
picking.message_post(body=message)
else:
message = ("<b>Auto validation Failed</b> <br/> <b>Reason:</b> There are no Batches/Serial no's available for <a href=# data-oe-model=product.product data-oe-id=%d>%s</a> product") % (product.id,product.name)
picking.message_post(body=message)
return False
@api.multi
def action_move_create(self):
#This method is overriden to pass the Discount Journal Entry.
""" Creates invoice related analytics and financial move lines """
account_move = self.env['account.move']
for inv in self:
if not inv.journal_id.sequence_id:
raise UserError(_('Please define sequence on the journal related to this invoice.'))
if not inv.invoice_line_ids:
raise UserError(_('Please create some invoice lines.'))
if inv.move_id:
continue
ctx = dict(self._context, lang=inv.partner_id.lang)
if not inv.date_invoice:
inv.with_context(ctx).write({'date_invoice': fields.Date.context_today(self)})
company_currency = inv.company_id.currency_id
# create move lines (one per invoice line + eventual taxes and analytic lines)
iml = inv.invoice_line_move_line_get()
iml += inv.tax_line_move_line_get()
diff_currency = inv.currency_id != company_currency
# create one move line for the total and possibly adjust the other lines amount
total, total_currency, iml = inv.with_context(ctx).compute_invoice_totals(company_currency, iml)
name = inv.name or '/'
if inv.payment_term_id:
totlines = inv.with_context(ctx).payment_term_id.with_context(currency_id=company_currency.id).compute(total, inv.date_invoice)[0]
res_amount_currency = total_currency
ctx['date'] = inv._get_currency_rate_date()
for i, t in enumerate(totlines):
if inv.currency_id != company_currency:
amount_currency = company_currency.with_context(ctx).compute(t[1], inv.currency_id)
else:
amount_currency = False
# last line: add the diff
res_amount_currency -= amount_currency or 0
if i + 1 == len(totlines):
amount_currency += res_amount_currency
iml.append({
'type': 'dest',
'name': name,
'price': t[1],
'account_id': inv.account_id.id,
'date_maturity': t[0],
'amount_currency': diff_currency and amount_currency,
'currency_id': diff_currency and inv.currency_id.id,
'invoice_id': inv.id
})
else:
iml.append({
'type': 'dest',
'name': name,
'price': total,
'account_id': inv.account_id.id,
'date_maturity': inv.date_due,
'amount_currency': diff_currency and total_currency,
'currency_id': diff_currency and inv.currency_id.id,
'invoice_id': inv.id
})
part = self.env['res.partner']._find_accounting_partner(inv.partner_id)
line = [(0, 0, self.line_get_convert(l, part.id)) for l in iml]
line = inv.group_lines(iml, line)
journal = inv.journal_id.with_context(ctx)
line = inv.finalize_invoice_move_lines(line)
date = inv.date or inv.date_invoice
move_vals = {
'ref': inv.reference,
'line_ids': line,
'journal_id': journal.id,
'date': date,
'narration': inv.comment,
}
ctx['company_id'] = inv.company_id.id
ctx['invoice'] = inv
ctx_nolang = ctx.copy()
ctx_nolang.pop('lang', None)
move = account_move.with_context(ctx_nolang).create(move_vals)
#=============Customized code starts=========
if inv.discount:
if inv.type == 'out_refund':
move_line = move.line_ids.filtered(lambda l:l.name==inv.name)
move_line.credit -= inv.discount
move_line_vals = {
'name':'Discount',
'company_id':move.company_id.id,
'account_id':inv.disc_acc_id.id,
'credit':inv.discount,
'date_maturity':date,
'currency_id': diff_currency and inv.currency_id.id,
'invoice_id': inv.id,
'partner_id':move_line.partner_id.id,
'move_id':move.id,
}
self.env['account.move.line'].create(move_line_vals)
else:
move_line = move.line_ids.filtered(lambda l:l.name=='/')
move_line.debit -= inv.discount
move_line_vals = {
'name':'Discount',
'company_id':move.company_id.id,
'account_id':inv.disc_acc_id.id,
'debit':inv.discount,
'date_maturity':date,
'currency_id': diff_currency and inv.currency_id.id,
'invoice_id': inv.id,
'partner_id':move_line.partner_id.id,
'move_id':move.id,
}
self.env['account.move.line'].create(move_line_vals)
#===========Customized code ends=============
# Pass invoice in context in method post: used if you want to get the same
# account move reference when creating the same invoice after a cancelled one:
move.post()
# make the invoice point to that move
vals = {
'move_id': move.id,
'date': date,
'move_name': move.name,
}
inv.with_context(ctx).write(vals)
return True
@api.model
def _prepare_refund(self, invoice, date_invoice=None, date=None, description=None, journal_id=None):
""" Prepare the dict of values to create the new refund from the invoice.
This method may be overridden to implement custom
refund generation (making sure to call super() to establish
a clean extension chain).
:param record invoice: invoice to refund
:param string date_invoice: refund creation date from the wizard
:param integer date: force date from the wizard
:param string description: description of the refund from the wizard
:param integer journal_id: account.journal from the wizard
:return: dict of value to create() the refund
"""
values = {}
for field in self._get_refund_copy_fields():
if invoice._fields[field].type == 'many2one':
values[field] = invoice[field].id
else:
values[field] = invoice[field] or False
values['invoice_line_ids'] = self._refund_cleanup_lines(invoice.invoice_line_ids)
tax_lines = invoice.tax_line_ids
taxes_to_change = {
line.tax_id.id: line.tax_id.refund_account_id.id
for line in tax_lines.filtered(lambda l: l.tax_id.refund_account_id != l.tax_id.account_id)
}
cleaned_tax_lines = self._refund_cleanup_lines(tax_lines)
values['tax_line_ids'] = self._refund_tax_lines_account_change(cleaned_tax_lines, taxes_to_change)
if journal_id:
journal = self.env['account.journal'].browse(journal_id)
elif invoice['type'] == 'in_invoice':
journal = self.env['account.journal'].search([('type', '=', 'purchase')], limit=1)
else:
journal = self.env['account.journal'].search([('type', '=', 'sale')], limit=1)
values['journal_id'] = journal.id
values['type'] = TYPE2REFUND[invoice['type']]
values['date_invoice'] = date_invoice or fields.Date.context_today(invoice)
values['state'] = 'draft'
values['number'] = False
values['origin'] = invoice.number
values['payment_term_id'] = False
values['refund_invoice_id'] = invoice.id
#=============Customized code starts========= Added Custom discount fields in refund
values['discount_type'] = invoice.discount_type
values['discount'] = invoice.discount
values['discount_percentage'] = invoice.discount_percentage
values['disc_acc_id'] = invoice.disc_acc_id.id
#===========Customized code ends=============
if date:
values['date'] = date
if description:
values['name'] = description
return values