This commit is contained in:
travelershot 2024-12-12 22:37:39 +06:00
commit 70dda814aa
2270 changed files with 912039 additions and 0 deletions

15
bahmni-addons/LICENSE Normal file
View File

@ -0,0 +1,15 @@
Additional modules for Odoo for Bahmni specific integrations
Copyright (C) 2014 OpenMRS, Inc
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
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 for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

2
bahmni-addons/NOTICE Normal file
View File

@ -0,0 +1,2 @@
Copyright (C) 2013 OpenMRS, Inc
This product includes software developed under the stewardship of the Bahmni Coalition, under fiscal sponsorship of OpenMRS, Inc. (http://www.openmrs.org/)

2
bahmni-addons/README.md Normal file
View File

@ -0,0 +1,2 @@
# odoo-modules
Custom Odoo modules (extensions) for Bahmni

View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
import models
import report

View File

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
{
'name': 'Bahmni Account',
'version': '1.0',
'summary': 'Custom account module to meet bahmni requirement',
'sequence': 1,
'description': """
Bahmni Account
====================
""",
'category': 'Account',
'website': '',
'images': [],
'depends': ['account', 'account_voucher', 'web_readonly_bypass'],
'data': [
'views/bahmni_account.xml',
'views/account_invoice_view.xml',
'views/account_config_settings.xml',
'report/account_count_report.xml',
'report/account_report.xml',
'security/ir.model.access.csv',
'views/account_payment.xml',
'report/report_invoice_inherit.xml'
],
'demo': [],
'qweb': [],
'installable': True,
'application': True,
'auto_install': False,
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!--Account for debiting discount amount-->
<record id="account_discount" model="account.account">
<field name="name">Discount</field>
</record>
<!--Account for debiting amount which is paid extra-->
<record id="account_overcharge" model="account.account">
<field name="name">Overcharge</field>
</record>
</odoo>

View File

@ -0,0 +1,19 @@
1. New fields added to account.invoice for applying discounts on invoices.
1. Discount Type : None, Fixed, Percentage
2. Discount : Total discount amount getting applied.
3. Discount Percentage : If percentage type is selected, then for defining percentage.
- if discount type is selected as percentage, then discount field will be readonly,
as discount amount will get computed based on given percentage.
4. Discount Account Head : Account from/to which discount amount will get credited or debited.
2. on change method is defined for discount percentage and invoice lines, to compute discount amount, when type percetage is selected.
3. Customer Invoices form is inherited, and footer is defined completely in new way,
as default footer class was not making form look good.
4. compute_amount method is overridden to change the logic of computing total of invoice, i.e. deduct discount amount from total.
5. create method of account_invoice_line is inherited to set value of discount field,
when invoice is created through Create Invoice button on Sale Order form.
As method which is getting called through that button, is creating invoice object first
and then one-by-one invoice lines are getting created.
6. rouding.off class defined to set round_off_amount value in account_invoice and sale_order.
for calculating this value, configuration is provided to user under Sales > Configuration > Settings menu,
where user can set Round Off Value, this is the amount to which value has to get rounded off.

View File

@ -0,0 +1,3 @@
* rounding.off class is kept in bahmni_account module, as it is used in account_invoice and sale_order class.
bahmni_sale already has dependency of bahmni_account module, hence this class can be accessed in that module.
vice-versa won't happen; as it will be a round dependency.

View File

@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
import rounding_off
import account_invoice
import account_invoice_line
import account_config_settings
import res_company
import account_payment

Binary file not shown.

View File

@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
class AccountConfigSettings(models.TransientModel):
_inherit = 'account.config.settings'
round_off_by = fields.Float(string="Round off by", related="company_id.round_off_by")
@api.multi
def set_round_off_by_defaults(self):
return self.env['ir.values'].sudo().set_default(
'account.config.settings', 'round_off_by', self.round_off_by)

View File

@ -0,0 +1,286 @@
# -*- 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

View File

@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
from odoo import models, api, fields
class AccountInvoiceLine(models.Model):
_inherit = 'account.invoice.line'
@api.model
def create(self, vals):
'''This method is overridden to update discount amount in invoice,
when invoice is getting created from sale order, and discount type selected in sale order is percentage.
Since, discount amount in readonly field and it gets updated by onchange method, which won't get called when invoice is created from backend.'''
if vals.get('invoice_id'):
invoice_obj = self.env['account.invoice'].browse(vals.get('invoice_id'))
amount_untaxed = 0.0
amount_tax = 0.0
# calculating total from already created invoice lines.
for ln in invoice_obj.invoice_line_ids:
amount_untaxed += ln.price_subtotal
taxes = ln.invoice_line_tax_ids.compute_all(ln.price_subtotal, invoice_obj.currency_id,
ln.quantity, product=ln.product_id,
partner=invoice_obj.partner_shipping_id)
amount_tax += sum(t.get('amount', 0.0) for t in taxes.get('taxes', []))
if vals.get('invoice_line_tax_ids'):
price_unit = vals.get('price_unit') * vals.get('quantity')
if vals.get('discount'):
price_unit = price_unit * (1 - vals['discount']/100)
amount_untaxed += price_unit
tax_ids = []
if len(vals['invoice_line_tax_ids'][0]) == 3:
tax_ids = vals['invoice_line_tax_ids'][0][2]
elif len(vals['invoice_line_tax_ids'][0]) == 1:
tax_ids = vals['invoice_line_tax_ids'][0]
tax_obj = self.env['account.tax'].browse(tax_ids)
taxes = tax_obj.compute_all(price_unit, invoice_obj.currency_id,
vals.get('quantity'), product=vals.get('product_id'),
partner=invoice_obj.partner_id)
amount_tax += sum(t.get('amount', 0.0) for t in taxes.get('taxes', []))
if invoice_obj.discount_type == 'percentage':
discount_amount = (invoice_obj.currency_id.round(amount_untaxed) +
invoice_obj.currency_id.round(amount_tax)) * invoice_obj.discount_percentage / 100
invoice_obj.write({'discount': discount_amount})
return super(AccountInvoiceLine, self).create(vals)

View File

@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
from itertools import groupby
from odoo import models, fields, api
class AccountAbstractPayment(models.AbstractModel):
_inherit = 'account.abstract.payment'
# commented as per Ajeenckya's suggestion, as keeping bank journals in selection, will fulfill the generic way of using payment
# journal_id = fields.Many2one('account.journal', string='Payment Journal', required=True,
# domain=[('type', '=', 'cash')])
class AccountPayment(models.Model):
_inherit = 'account.payment'
@api.onchange('partner_id', 'amount')
def _calculate_balances(self):
if(self.state != 'posted'):
partner = self.partner_id
balance = partner.credit or partner.debit
self.balance_before_pay = balance
self.total_balance = balance - self.amount
@api.onchange('invoice_ids')
def onchange_partner_id(self):
if self.invoice_ids:
bill_amount = 0
for inv in self.invoice_ids:
bill_amount += inv.amount_total
self.bill_amount = bill_amount
@api.onchange('payment_type')
def _onchange_payment_type(self):
if not self.invoice_ids:
# Set default partner type for the payment type
if self.payment_type == 'inbound':
self.partner_type = 'customer'
elif self.payment_type == 'outbound':
self.partner_type = 'supplier'
else:
self.partner_type = False
# Set payment method domain
res = self._onchange_journal()
if not res.get('domain', {}):
res['domain'] = {}
res['domain']['journal_id'] = self.payment_type == 'inbound' and [('at_least_one_inbound', '=', True)] or self.payment_type == 'outbound' and [('at_least_one_outbound', '=', True)] or []
#res['domain']['journal_id'].append(('type', '=', 'cash'))
return res
balance_before_pay = fields.Float(compute=_calculate_balances,
string="Balance before pay")
total_balance = fields.Float(compute=_calculate_balances,
string="Total Balance")
invoice_id = fields.Many2one('account.invoice', string='Invoice')
bill_amount = fields.Float(string="Bill Amount")

View File

@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
from odoo import models, fields
class ResCompany(models.Model):
_inherit = 'res.company'
round_off_by = fields.Float(string="Round off by")

Binary file not shown.

View File

@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
from odoo import models, fields
class ResPartner(models.Model):
_inherit = 'res.partner'
initials = fields.Char(string="Initials")

View File

@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
class RoundingOff(models.Model):
_name = 'rounding.off'
def round_off_value_to_nearest(self, value):
round_off_by = self.env['ir.values'].get_default('sale.config.settings', 'round_off_by')
if(round_off_by > 0):
half_round_off_by = round_off_by / 2.0
remainder = value % round_off_by
return -remainder if remainder < half_round_off_by\
else round_off_by - remainder
return 0

Binary file not shown.

View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
import account_count_report
import account_report

Binary file not shown.

View File

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
from odoo.tools import drop_view_if_exists
from odoo.exceptions import Warning
class AccountCountReport(models.Model):
_name = 'account.count.report'
_auto = False
_description = "Count of account heads in sale orders over a period"
count = fields.Integer(string="Count", readonly=True)
date = fields.Date(string="Date", readonly=True)
account_id = fields.Many2one('account.account', string="Account")
@api.model_cr
def init(self):
drop_view_if_exists(self.env.cr, 'account_count_report')
self.env.cr.execute("""
create or replace view account_count_report as (
select
concat(ail.account_id, '_', ai.date_invoice) as id,
ai.date_invoice as date,
ail.account_id as account_id,
count(*) as count
from account_invoice ai, account_invoice_line ail
where
ail.invoice_id = ai.id
and ai.type != 'out_refund'
group by ail.account_id, ai.date_invoice
)""")
@api.multi
def unlink(self):
raise Warning('You cannot delete any record!')

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_account_count_report_tree" model="ir.ui.view">
<field name="name">account.count.report.tree</field>
<field name="model">account.count.report</field>
<field name="arch" type="xml">
<tree string="Accounts Report">
<field name="date"/>
<field name="account_id"/>
<field name="count"/>
</tree>
</field>
</record>
<record id="view_account_count_report_filter" model="ir.ui.view">
<field name="name">account.count.report.filter</field>
<field name="model">account.count.report</field>
<field name="arch" type="xml">
<search string="Search Accounts Report">
<field name="account_id"/>
<filter string="Last 30 Days" name="filter_month"
domain="[
['date', '&lt;=', context_today().strftime('%%Y-%%m-%%d')],
['date', '&gt;=', (context_today() - datetime.timedelta(30)).strftime('%%Y-%%m-%%d')]
]" />
<filter string="Today" name="filter_today"
domain="[('date', '=', context_today().strftime('%%Y-%%m-%%d'))]"/>
<group string="Group By...">
<filter string="Account" name="groupby_account_head"
icon="terp-go-month" domain="[]" context="{'group_by':'account_id'}"/>
<filter string="Date" name="groupby_date_head"
icon="terp-go-month" domain="[]" context="{'group_by':'date'}"/>
</group>
</search>
</field>
</record>
<record id="action_search_account_count_reports" model="ir.actions.act_window">
<field name="name">Accounts Count Report</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.count.report</field>
<field name="view_type">form</field>
<field name="view_mode">tree</field>
<field name="view_id" ref="view_account_count_report_tree"/>
<field name="search_view_id" ref="view_account_count_report_filter"/>
<field name="context">{}</field>
</record>
<menuitem action="action_search_account_count_reports"
id="menu_action_search_account_count_reports"
parent="account.menu_finance_reports" groups="account.group_account_user" />
<!--<record id="filter_account_count_report_by_account_id" model="ir.filters">
<field name="name">Required Accounts Only</field>
<field name="model_id">account.count.report</field>
<field name="is_default">1</field>
<field name="user_id"></field>
<field name="domain">
[ '|', '|', '|',
['account_id', 'ilike', 'ECG'],
['account_id', 'ilike', 'X-Ray'],
['account_id', 'ilike', 'MTP'],
['account_id', 'ilike', 'Sonography']
]
</field>
</record>-->
</odoo>

View File

@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
from odoo.tools import drop_view_if_exists
class AccountReport(models.Model):
_name = 'account.report'
_description = "Account reports for actual & received amount"
_auto = False
actual_amount = fields.Integer(string="Expenses")
amount_received = fields.Integer(string="Collections")
date = fields.Date(string="Date")
account_id = fields.Many2one('account.account', string="Account Head")
@api.model_cr
def init(self):
drop_view_if_exists(self.env.cr, 'account_report')
self.env.cr.execute("""
create or replace view account_report as (
(select id,date,account_id, sum(actual_amount) as actual_amount,sum(amount_received) as amount_received
from (
SELECT
pg_catalog.concat(ail.account_id, '_', ai.date_invoice, '_', ai.type) AS id,ai.id as invoice_id,
ai.date_invoice AS date,
ail.account_id,
CASE
WHEN ai.type = 'out_refund' THEN 0
ELSE sum(ail.price_subtotal)
END AS actual_amount,
CASE
WHEN ai.type = 'out_refund' THEN sum(
(-ail.price_subtotal) * (ai.amount_total / (ai.amount_tax + ai.amount_untaxed)))
ELSE sum(ail.price_subtotal * (ai.amount_total / (ai.amount_tax + ai.amount_untaxed)))
END AS amount_received
FROM account_invoice ai, account_invoice_line ail
WHERE ail.invoice_id = ai.id AND (ai.amount_tax + ai.amount_untaxed) <> 0 AND state = 'paid'
GROUP BY ail.account_id, ai.date_invoice, ai.type, ai.id
UNION
SELECT
pg_catalog.concat(ail.account_id, '_', ai.date_invoice, '_', ai.type) AS id,ai.id as invoice_id,
ai.date_invoice AS date,
ail.account_id,
CASE
WHEN ai.type = 'out_refund' THEN 0
ELSE max(ai.amount_total)
END AS actual_amount,
CASE
WHEN ai.type = 'out_refund' THEN max(ai.amount_total) * -1
ELSE max(ai.amount_total)
END AS amount_received
FROM account_invoice ai, account_invoice_line ail
WHERE ail.invoice_id = ai.id AND (ai.amount_tax + ai.amount_untaxed) = 0 AND state = 'paid'
and ail.account_id not in
(select id from account_account where name in ('FINE','Discount','Overcharge'))
GROUP BY ail.account_id, ai.date_invoice, ai.type,ai.id
UNION
SELECT
pg_catalog.concat(ail.account_id, '_', ai.date_invoice, '_', ai.type) AS id,ai.id as invoice_id,
ai.date_invoice AS date,
ail.account_id,
CASE
WHEN ai.type = 'out_refund' THEN 0
ELSE sum(ai.amount_total)
END AS actual_amount,
CASE
WHEN ai.type = 'out_refund' THEN sum(-ai.amount_total)
ELSE sum(ai.amount_total)
END AS amount_received
FROM account_invoice ai, account_invoice_line ail
WHERE ail.invoice_id = ai.id AND (ai.amount_tax + ai.amount_untaxed) = 0 AND state = 'paid'
and ail.account_id in
(select id from account_account where name in ('FINE','Discount','Overcharge'))
GROUP BY ail.account_id, ai.date_invoice, ai.type,ai.id
) as r group by id,date,account_id)
)""")

Binary file not shown.

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_account_report_tree" model="ir.ui.view">
<field name="name">account.report.tree</field>
<field name="model">account.report</field>
<field name="arch" type="xml">
<tree string="Accounts Report">
<field name="date"/>
<field name="account_id"/>
<field name="amount_received" sum="Total Collections"/>
<field name="actual_amount" sum="Total Expense"/>
</tree>
</field>
</record>
<record id="view_account_report_filter" model="ir.ui.view">
<field name="name">account.report.filter</field>
<field name="model">account.report</field>
<field name="arch" type="xml">
<search string="Search Accounts Report">
<field name="account_id"/>
<filter string="Last 30 Days" name="filter_month"
domain="[
['date', '&lt;=', context_today().strftime('%%Y-%%m-%%d')],
['date', '&gt;=', (context_today() - datetime.timedelta(30)).strftime('%%Y-%%m-%%d')]
]" />
<filter string="Today" name="filter_today"
domain="[('date', '=', context_today().strftime('%%Y-%%m-%%d'))]"/>
<group string="Group By...">
<filter string="Account" name="groupby_account_head"
icon="terp-go-month" domain="[]" context="{'group_by':'account_id'}"/>
<filter string="Date" name="groupby_date_head"
icon="terp-go-month" domain="[]" context="{'group_by':'date'}"/>
</group>
</search>
</field>
</record>
<record id="action_search_account_reports" model="ir.actions.act_window">
<field name="name">Account Report</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.report</field>
<field name="view_type">form</field>
<field name="view_mode">tree</field>
<field name="view_id" ref="view_account_report_tree"/>
<field name="search_view_id" ref="view_account_report_filter"/>
<field name="context">{}</field>
</record>
<menuitem action="action_search_account_reports"
id="menu_action_search_account_reports"
parent="account.menu_finance_reports" groups="account.group_account_user"
sequence="3"/>
</odoo>

View File

@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<template id="account.report_invoice_document">
<t t-call="report.external_layout">
<t t-set="o" t-value="o.with_context({'lang':o.partner_id.lang})" />
<div class="page">
<div class="row">
<div name="invoice_address" class="col-xs-5 col-xs-offset-7">
<address t-field="o.partner_id"
t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}' />
<span t-if="o.partner_id.vat">TIN: <span t-field="o.partner_id.vat"/></span>
</div>
</div>
<h2>
<span t-if="o.type == 'out_invoice' and (o.state == 'open' or o.state == 'paid')">Invoice</span>
<span t-if="o.type == 'out_invoice' and o.state == 'proforma2'">PRO-FORMA</span>
<span t-if="o.type == 'out_invoice' and o.state == 'draft'">Draft Invoice</span>
<span t-if="o.type == 'out_invoice' and o.state == 'cancel'">Cancelled Invoice</span>
<span t-if="o.type == 'out_refund'">Refund</span>
<span t-if="o.type == 'in_refund'">Vendor Refund</span>
<span t-if="o.type == 'in_invoice'">Vendor Bill</span>
<span t-field="o.number"/>
</h2>
<div class="row mt32 mb32">
<div class="col-xs-2" t-if="o.name">
<strong>Description:</strong>
<p t-field="o.name"/>
</div>
<div class="col-xs-2" t-if="o.date_invoice">
<strong>Invoice Date:</strong>
<p t-field="o.date_invoice"/>
</div>
<div class="col-xs-2" t-if="o.date_due and o.type == 'out_invoice' and (o.state == 'open' or o.state == 'paid')">
<strong>Due Date:</strong>
<p t-field="o.date_due"/>
</div>
<div class="col-xs-2" t-if="o.origin">
<strong>Source:</strong>
<p t-field="o.origin"/>
</div>
<div class="col-xs-2" t-if="o.partner_id.ref">
<strong>Customer Code:</strong>
<p t-field="o.partner_id.ref"/>
</div>
<div name="reference" class="col-xs-2" t-if="o.reference">
<strong>Reference:</strong>
<p t-field="o.reference"/>
</div>
</div>
<!-- Is there a discount on at least one line? -->
<t t-set="display_discount" t-value="any([l.discount for l in o.invoice_line_ids])"/>
<table class="table table-condensed">
<thead>
<tr>
<th>Description</th>
<th class="hidden">Source Document</th>
<th class="text-right">Quantity</th>
<th class="text-right">Batch No</th>
<th class="text-right">Expiry Date</th>
<th class="text-right">Unit Price</th>
<th t-if="display_discount" class="text-right">Disc.(%)</th>
<th class="text-right">Taxes</th>
<th class="text-right">Tax Excluded Price</th>
</tr>
</thead>
<tbody class="invoice_tbody">
<tr t-foreach="o.invoice_line_ids" t-as="l">
<td><span t-field="l.name"/></td>
<td class="hidden"><span t-field="l.origin"/></td>
<td class="text-right">
<span t-field="l.quantity"/>
<span t-field="l.uom_id" groups="product.group_uom"/>
</td>
<td class="text-right">
<span t-field="l.lot_id.name"/>
</td>
<td class="text-right">
<span t-field="l.expiry_date"/>
</td>
<td class="text-right">
<span t-field="l.price_unit"/>
</td>
<td t-if="display_discount" class="text-right">
<span t-field="l.discount"/>
</td>
<td class="text-right">
<span t-esc="', '.join(map(lambda x: (x.description or x.name), l.invoice_line_tax_ids))"/>
</td>
<td class="text-right">
<span t-field="l.price_subtotal"
t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
</td>
</tr>
</tbody>
</table>
<div class="row">
<div class="col-xs-4 pull-right">
<table class="table table-condensed">
<tr class="border-black">
<td><strong>Subtotal</strong></td>
<td class="text-right">
<span t-field="o.amount_untaxed" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
</td>
</tr>
<t t-foreach="o._get_tax_amount_by_group()" t-as="amount_by_group">
<tr>
<td><span t-esc="amount_by_group[0] if len(o.tax_line_ids) > 1 else (o.tax_line_ids.tax_id.description or o.tax_line_ids.tax_id.name)"/></td>
<td class="text-right">
<span t-esc="amount_by_group[2]"/>
</td>
</tr>
</t>
<tr class="border-black">
<td><strong>Total</strong></td>
<td class="text-right">
<span t-field="o.amount_total" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
</td>
</tr>
</table>
</div>
</div>
<!-- DO NOT REMOVE THIS TABLE. MANDATORY IN SOME COUNTRIES -->
<div class="row" t-if="len(o.tax_line_ids) > 0">
<div class="col-xs-6">
<table class="table table-condensed">
<thead>
<tr>
<th>Tax</th>
<th class="text-right">Base</th>
<th class="text-right">Amount</th>
</tr>
</thead>
<tbody>
<tr t-foreach="o.tax_line_ids" t-as="t">
<td><span t-field="t.tax_id.description"/></td>
<td class="text-right">
<span t-field="t.base" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
</td>
<td class="text-right">
<span t-field="t.amount" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p t-if="o.comment">
<strong>Comment:</strong>
<span t-field="o.comment"/>
</p>
<p t-if="o.payment_term_id">
<span t-field="o.payment_term_id.note"/>
</p>
<p t-if="o.fiscal_position_id.note">
<strong>Fiscal Position Remark:</strong>
<span t-field="o.fiscal_position_id.note"/>
</p>
</div>
</t>
</template>
</odoo>

View File

@ -0,0 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_account_count_report_account_user,Account Count Report Account User,model_account_count_report,account.group_account_user,1,1,1,0
access_rounding_off,access_rounding_off,model_rounding_off,,1,1,1,1
access_account_report,access_account_report,model_account_report,,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_account_count_report_account_user Account Count Report Account User model_account_count_report account.group_account_user 1 1 1 0
3 access_rounding_off access_rounding_off model_rounding_off 1 1 1 1
4 access_account_report access_account_report model_account_report 1 1 1 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,20 @@
/* css code for sale order form footer */
.footer_label{
padding-right:0px;
white-space: nowrap;
}
.footer_label > label:after{
content: ": "
}
.footer_field{
padding-right:0px;
float: right !important;
}
.footer_text_bold{
font-size: 1.3em;
padding-right: 80px;
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="inherit_view_account_config_settings" model="ir.ui.view">
<field name="name">inherit.view.account.config.settings</field>
<field name="model">account.config.settings</field>
<field name="inherit_id" ref="account.view_account_config_settings"/>
<field name="arch" type="xml">
<xpath expr="//separator[@name='analytic_account']" position="before">
<group>
<label for="round_off_by"/>
<field name="round_off_by" nolabel="1"/>
</group>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1,186 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="inherit_invoice_form" model="ir.ui.view">
<field name="name">inherit.invoice.form</field>
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook/page/group" position="replace">
<div class="col-xs-5 pull-right text-right" style="padding-right:0px">
<div class="col-xs-6 footer_label" style="min-height: 28px;">
<label for="amount_untaxed" />
</div>
<div class="col-xs-6 footer_field" style="min-height: 28px;">
<field name="amount_untaxed"/>
</div>
<div class="col-xs-6 footer_label" style="min-height: 28px;">
<label for="amount_tax"/>
</div>
<div class="col-xs-6 footer_field" style="min-height: 28px;">
<field name="amount_tax"/>
</div>
<div class="col-xs-6 footer_label" style="min-height: 28px;">
<label for="discount_type"/>
</div>
<div class="col-xs-6 footer_field" style="min-height: 28px;">
<field name="discount_type" required="1"/>
</div>
<div class="col-xs-6 footer_label" style="min-height: 28px;">
<label for="round_off_amount"/>
</div>
<div class="col-xs-6 footer_field" style="min-height: 28px;">
<field name="round_off_amount"/>
</div>
<div class="col-xs-6 footer_label" style="min-height: 28px;"
attrs="{'invisible': [('discount_type', '!=', 'percentage')]}">
<label for="discount_percentage" />
</div>
<div class="col-xs-6 footer_field" style="white-space:nowrap;padding-right:15px"
attrs="{'invisible': [('discount_type', '!=', 'percentage')]}" >
<field name="discount_percentage" /> <b style="padding-left:2px">%</b>
</div>
<div class="col-xs-6 footer_label" style="min-height:33px;"
attrs="{'invisible': [('discount_type', '=', 'none')]}">
<label for="discount" />
</div>
<div class="col-xs-6 footer_field" style="min-height:33px;"
attrs="{'invisible': [('discount_type', '=', 'none')]}">
<field name="discount" style="width:100% !important" context="{'readonly_by_pass': True}"
attrs="{'readonly': [('discount_type', '=', 'percentage')]}"/>
</div>
<div class="col-xs-6 footer_label"
attrs="{'invisible': [('discount_type', '=', 'none')]}">
<label for="disc_acc_id" name="remove_display_prop"/>
</div>
<div class="col-xs-6 footer_field" attrs="{'invisible': [('discount_type', '=', 'none')]}">
<field name="disc_acc_id"
attrs="{'required': [('discount_type', 'in', ('fixed', 'percentage'))]}"/>
</div>
<group style="width: 100%; border-top: 1px solid #cacaca;
font-weight: bold; white-space:nowrap;">
<div class="col-xs-6 oe_right footer_label">
<label for="amount_total" style="font-weight:bold"/>
<button name="button_dummy" states="draft,sent" string="(update)"
type="object" class="oe_edit_only oe_link"/>
</div>
<div class="col-xs-6 footer_field footer_text_bold" >
<field name="amount_total" />
</div>
</group>
<group style="width: 100%; border-top: 1px solid #cacaca;
font-weight: bold; white-space:nowrap;">
<div class="col-xs-6 oe_right footer_label">
<label for="residual" style="font-weight:bold"/>
</div>
<div class="col-xs-6 footer_field footer_text_bold" >
<field name="residual" />
</div>
</group>
</div>
</xpath>
</field>
</record>
<record id="account.action_invoice_tree1" model="ir.actions.act_window">
<field name="name">Customer Invoices</field>
<field name="res_model">account.invoice</field>
<field name="view_type">form</field>
<field name="view_mode">tree,kanban,form,calendar,pivot,graph</field>
<field eval="False" name="view_id"/>
<field name="domain">[('type','in',('out_invoice', 'out_refund'))]</field>
<field name="context">{'type':'out_invoice', 'journal_type': 'sale',
'readonly_by_pass': True}
</field>
<field name="search_view_id" ref="account.view_account_invoice_filter"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a customer invoice.
</p>
<p>
Odoo's electronic invoicing allows to ease and fasten the
collection of customer payments. Your customer receives the
invoice by email and he can pay online and/or import it
in his own system.
</p>
<p>
The discussions with your customer are automatically displayed at
the bottom of each invoice.
</p>
</field>
</record>
<!--Supplier Form-->
<record id="inherit_invoice_supplier_form" model="ir.ui.view">
<field name="name">inherit.invoice.supplier.form</field>
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_supplier_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook/page/group" position="replace">
<div class="col-xs-5 pull-right text-right" style="padding-right:0px">
<div class="col-xs-6 footer_label" style="min-height: 28px;">
<label for="amount_untaxed" />
</div>
<div class="col-xs-6 footer_field" style="min-height: 28px;">
<field name="amount_untaxed"/>
</div>
<div class="col-xs-6 footer_label" style="min-height: 28px;">
<label for="amount_tax"/>
</div>
<div class="col-xs-6 footer_field" style="min-height: 28px;">
<field name="amount_tax"/>
</div>
<div class="col-xs-6 footer_label" style="min-height: 28px;">
<label for="discount_type"/>
</div>
<div class="col-xs-6 footer_field" style="min-height: 28px;">
<field name="discount_type" required="1"/>
</div>
<div class="col-xs-6 footer_label" style="min-height: 28px;">
<label for="round_off_amount"/>
</div>
<div class="col-xs-6 footer_field" style="min-height: 28px;">
<field name="round_off_amount"/>
</div>
<div class="col-xs-6 footer_label" style="min-height: 28px;"
attrs="{'invisible': [('discount_type', '!=', 'percentage')]}">
<label for="discount_percentage" />
</div>
<div class="col-xs-6 footer_field" style="white-space:nowrap;padding-right:15px"
attrs="{'invisible': [('discount_type', '!=', 'percentage')]}" >
<field name="discount_percentage" /> <b style="padding-left:2px">%</b>
</div>
<div class="col-xs-6 footer_label" style="min-height:33px;"
attrs="{'invisible': [('discount_type', '=', 'none')]}">
<label for="discount" />
</div>
<div class="col-xs-6 footer_field" style="min-height:33px;"
attrs="{'invisible': [('discount_type', '=', 'none')]}">
<field name="discount" style="width:100% !important" context="{'readonly_by_pass': True}"
attrs="{'readonly': [('discount_type', '=', 'percentage')]}"/>
</div>
<div class="col-xs-6 footer_label"
attrs="{'invisible': [('discount_type', '=', 'none')]}">
<label for="disc_acc_id" name="remove_display_prop"/>
</div>
<div class="col-xs-6 footer_field" attrs="{'invisible': [('discount_type', '=', 'none')]}">
<field name="disc_acc_id"
attrs="{'required': [('discount_type', 'in', ('fixed', 'percentage'))]}"/>
</div>
<group style="width: 100%; border-top: 1px solid #cacaca;
font-weight: bold; white-space:nowrap;">
<div class="col-xs-6 oe_right footer_label">
<label for="amount_total" />
<button name="button_dummy" states="draft,sent" string="(update)"
type="object" class="oe_edit_only oe_link"/>
</div>
<div class="col-xs-6 footer_field footer_text_bold" >
<field name="amount_total" />
</div>
</group>
</div>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_account_payment_form_inherit" model="ir.ui.view">
<field name="name">view.account.payment.form.inherit</field>
<field name="model">account.payment</field>
<field name="inherit_id" ref="account.view_account_payment_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='journal_id']" position="before">
<field name="balance_before_pay" string="Amount Due"/>
<field name="bill_amount"/>
<field name="total_balance"/>
<field name="invoice_id"/>
</xpath>
<xpath expr="//field[@name='journal_id']" position="replace">
<field name="journal_id" widget="selection"
attrs="{'readonly': [('state', '!=', 'draft')]}"/>
</xpath>
</field>
</record>
<record id="view_register_payment_inherit" model="ir.ui.view">
<field name="name">view.register.payment.inherit</field>
<field name="model">account.payment</field>
<field name="inherit_id" ref="account.view_account_payment_invoice_form"/>
<field name="arch" type="xml">
<xpath expr="//label[@for='amount']" position="before">
<field name="balance_before_pay" readonly="1"/>
</xpath>
<xpath expr="//div[@name='amount_div']" position="after">
<field name="bill_amount" readonly="1"/>
<field name="total_balance" readonly="1"/>
</xpath>
<xpath expr="//field[@name='journal_id']" position="replace">
<field name="journal_id" widget="selection"/>
</xpath>
<xpath expr="//button[@name='post']" position="replace">
<button string="Validate" name="post" type="object" class="btn-primary"
context="{'readonly_by_pass': True}"/>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<template id="assets_backend" name="bahmni_account assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/bahmni_account/static/src/css/account_invoice_footer.css"/>
<!-- <script type="text/javascript" src="/bahmni_account/static/src/js/sale_order_footer.js"></script-->
<!-- <script type="text/javascript" src="/account/static/src/js/tour_bank_statement_reconciliation.js"></script> -->
</xpath>
</template>
</data>
</odoo>

View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
import models
import wizard

View File

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
{
'name': 'Bahmni Atom Feed',
'version': '1.0',
'summary': 'Module to sync Bahmni with Odoo',