Houssine BAKKALI
4 years ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1413 additions and 188 deletions
-
15easy_my_coop_loan/__manifest__.py
-
12easy_my_coop_loan/data/loan_data.xml
-
8easy_my_coop_loan/data/mail_template_data.xml
-
33easy_my_coop_loan/models/interest_line.py
-
73easy_my_coop_loan/models/loan.py
-
148easy_my_coop_loan/models/loan_issue_line.py
-
111easy_my_coop_loan/report/loan_issue_line_report.xml
-
13easy_my_coop_loan/report/loan_report.xml
-
2easy_my_coop_loan/security/ir.model.access.csv
-
96easy_my_coop_loan/views/loan_interest_lines_view.xml
-
114easy_my_coop_loan/views/loan_line_view.xml
-
111easy_my_coop_loan/views/loan_view.xml
-
2easy_my_coop_loan_account/__init__.py
-
29easy_my_coop_loan_account/__manifest__.py
-
31easy_my_coop_loan_account/data/emc_loan_account_data.xml
-
5easy_my_coop_loan_account/models/__init__.py
-
46easy_my_coop_loan_account/models/account_fiscal_year.py
-
39easy_my_coop_loan_account/models/account_move.py
-
92easy_my_coop_loan_account/models/company.py
-
200easy_my_coop_loan_account/models/interest_line.py
-
71easy_my_coop_loan_account/models/loan_issue_line.py
-
22easy_my_coop_loan_account/views/loan_issue_line_view.xml
-
25easy_my_coop_loan_account/views/res_company_view.xml
-
1easy_my_coop_loan_account/wizard/__init__.py
-
77easy_my_coop_loan_account/wizard/end_of_year_operation.py
-
35easy_my_coop_loan_account/wizard/end_of_year_operation.xml
-
1easy_my_coop_loan_bba/__init__.py
-
21easy_my_coop_loan_bba/__manifest__.py
-
1easy_my_coop_loan_bba/models/__init__.py
-
95easy_my_coop_loan_bba/models/loan_issue_line.py
-
11easy_my_coop_loan_website/__manifest__.py
-
26easy_my_coop_loan_website/controllers/main.py
-
6easy_my_coop_website/__manifest__.py
-
9easy_my_coop_website_portal/__manifest__.py
-
6easy_my_coop_website_portal/controllers/main.py
-
14easy_my_coop_website_portal/views/easy_my_coop_website_portal_templates.xml
@ -0,0 +1,12 @@ |
|||
<odoo noupdate="1"> |
|||
<record id="sequence_loan_issue_line" model="ir.sequence"> |
|||
<field name="name">Loan issue line</field> |
|||
<field name="code">loan.issue.line.sequence</field> |
|||
<field name="prefix">LOAN/%(year)s/</field> |
|||
<field eval="1" name="number_next"/> |
|||
<field eval="1" name="number_increment"/> |
|||
<field eval="True" name="use_date_range"/> |
|||
<field eval="False" name="company_id"/> |
|||
<field name="padding">4</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,111 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
|
|||
<template id="loan_issue_line_document"> |
|||
|
|||
<t t-call="web.external_layout"> |
|||
<t t-set="address"> |
|||
<address t-field="o.partner_id" |
|||
t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}'/> |
|||
</t> |
|||
<div class="page mt32"> |
|||
<h2> |
|||
<span>Reimbursement table</span> |
|||
</h2> |
|||
|
|||
<p name="loan_payment_date_date" class="mt16"> |
|||
<span>Loan payment received on</span> |
|||
<span t-field="o.payment_date"/> |
|||
</p> |
|||
|
|||
<table class="table table-sm o_main_table mt16" |
|||
name="invoice_line_table"> |
|||
<thead> |
|||
<tr> |
|||
<t t-set="colspan" t-value="5"/> |
|||
<th class="text-left">Year</th> |
|||
<th class="text-right">Subscribed amount</th> |
|||
<th class="text-right">Accrued amount</th> |
|||
<th class="text-right">Gross interest</th> |
|||
<th class="text-right">Accrued gross interest</th> |
|||
<th class="text-right">Net interest</th> |
|||
<th class="text-right">Tax amount</th> |
|||
<th class="text-right">Due amount</th> |
|||
<th class="text-right">State</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody class="invoice_tbody"> |
|||
<t t-foreach="o.interest_lines" t-as="line"> |
|||
<tr> |
|||
<td class="text-left" name="name"> |
|||
<span t-field="line.name"/> |
|||
</td> |
|||
<td class="text-right" name="subscribed_amount"> |
|||
<span t-field="line.amount"/> |
|||
</td> |
|||
<td class="text-right" name="accrued_amount"> |
|||
<span t-field="line.accrued_amount"/> |
|||
</td> |
|||
<td class="text-right" name="interest"> |
|||
<span t-field="line.interest" |
|||
t-options='{"widget": "monetary", "display_currency": o.loan_issue_id.company_currency_id}'/> |
|||
</td> |
|||
<td class="text-right" name="accrued_interest"> |
|||
<span t-field="line.accrued_interest" |
|||
t-options='{"widget": "monetary", "display_currency": o.loan_issue_id.company_currency_id}'/> |
|||
</td> |
|||
<td class="text-right" name="net_interest"> |
|||
<span t-field="line.net_interest" |
|||
t-options='{"widget": "monetary", "display_currency": o.loan_issue_id.company_currency_id}'/> |
|||
</td> |
|||
<td class="text-right" name="taxes_amount"> |
|||
<span t-field="line.taxes_amount" |
|||
t-options='{"widget": "monetary", "display_currency": o.loan_issue_id.company_currency_id}'/> |
|||
</td> |
|||
<td class="text-right" name="due_amount"> |
|||
<span t-field="line.due_amount" |
|||
t-options='{"widget": "monetary", "display_currency": o.loan_issue_id.company_currency_id}'/> |
|||
</td> |
|||
<td class="text-right" name="due_date"> |
|||
<span t-field="line.due_date"/> |
|||
</td> |
|||
<td class="text-right" name="state"> |
|||
<span t-field="line.state"/> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
|
|||
<div class="row mt32"> |
|||
<div name="board_commitee" class="col-sm-6"> |
|||
<p> |
|||
For the board of<span t-field="o.company_id.name"/>. |
|||
</p> |
|||
<p> |
|||
<strong> |
|||
<span t-field="o.company_id.board_representative"/> |
|||
</strong> |
|||
</p> |
|||
<img t-if="o.company_id.signature_scan" |
|||
t-attf-class="mt16 w-50" |
|||
t-attf-style="{{ 'min-width: 100px; max-width: 250px' if report_type == 'pdf' else '' }}" |
|||
t-att-src="'data:image/png;base64,%s' % o.company_id.signature_scan.decode()"/> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</t> |
|||
</template> |
|||
|
|||
|
|||
<template id="loan_issue_line"> |
|||
<t t-call="web.html_container"> |
|||
<t t-foreach="docs" t-as="o"> |
|||
<t t-call="easy_my_coop_loan.loan_issue_line_document" |
|||
t-lang="o.partner_id.lang"/> |
|||
</t> |
|||
</t> |
|||
</template> |
|||
|
|||
</odoo> |
@ -0,0 +1,13 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<report |
|||
id="action_loan_issue_line_report" |
|||
model="loan.issue.line" |
|||
string="Reimbursment table report" |
|||
report_type="qweb-pdf" |
|||
name="easy_my_coop_loan.loan_issue_line" |
|||
file="easy_my_coop_loan.loan_issue_line_report.xml" |
|||
multi="True" |
|||
menu="True" |
|||
/> |
|||
</odoo> |
@ -0,0 +1,96 @@ |
|||
<odoo> |
|||
<record id="action_loan_interest_lines" model="ir.actions.act_window"> |
|||
<field name="name">Loan interest lines</field> |
|||
<field name="res_model">loan.interest.line</field> |
|||
<field name="view_type">form</field> |
|||
<field name="view_mode">tree,form</field> |
|||
</record> |
|||
|
|||
<record id="loan_interest_line_view_tree" model="ir.ui.view"> |
|||
<field name="name">loan.interest.line.view.tree</field> |
|||
<field name="model">loan.interest.line</field> |
|||
<field name="arch" type="xml"> |
|||
<tree string="Loan interest lines"> |
|||
<field name="issue_line" invisible="True"/> |
|||
<field name="name"/> |
|||
<field name="amount"/> |
|||
<field name="accrued_amount"/> |
|||
<field name="interest"/> |
|||
<field name="accrued_interest"/> |
|||
<field name="net_interest"/> |
|||
<field name="taxes_amount"/> |
|||
<field name="due_loan_amount"/> |
|||
<field name="due_amount"/> |
|||
<field name="due_date"/> |
|||
<field name="state"/> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="view_loan_interest_line_form" model="ir.ui.view"> |
|||
<field name="name">loan.interest.line.form</field> |
|||
<field name="model">loan.interest.line</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Loan interest line"> |
|||
<header> |
|||
<field name="state" widget="statusbar"/> |
|||
</header> |
|||
<sheet> |
|||
<group> |
|||
<group> |
|||
<field name="issue_line"/> |
|||
<field name="name"/> |
|||
<field name="amount"/> |
|||
<field name="accrued_amount"/> |
|||
<field name="due_loan_amount"/> |
|||
<field name="due_amount"/> |
|||
<field name="due_date"/> |
|||
</group> |
|||
<group> |
|||
<field name="interest"/> |
|||
<field name="net_interest"/> |
|||
<field name="taxes_amount"/> |
|||
<field name="accrued_interest"/> |
|||
<field name="accrued_net_interest"/> |
|||
</group> |
|||
</group> |
|||
</sheet> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="view_loan_interest_line_filter" model="ir.ui.view"> |
|||
<field name="name">Loans interest lines Search</field> |
|||
<field name="model">loan.interest.line</field> |
|||
<field name="arch" type="xml"> |
|||
<search string="Search Loan interest lines"> |
|||
<field name="name"/> |
|||
<field name="partner_id"/> |
|||
<field name="loan_issue_id"/> |
|||
<separator/> |
|||
<filter string="Draft" name="state_draft" |
|||
domain="[('state','=','draft')]"/> |
|||
<filter string="Due" name="state_due" |
|||
domain="[('state','=','due')]"/> |
|||
<filter string="Requested" name="state_requested" |
|||
domain="[('state','=','requested')]"/> |
|||
<filter string="Donation" name="state_donation" |
|||
domain="[('state','=','donation')]"/> |
|||
<filter string="Paid" name="state_paid" |
|||
domain="[('state','=','paid')]"/> |
|||
<group expand="0" name="group_by" string="Group By"> |
|||
<filter name="loan_issue_id" string="Loan issue" |
|||
context="{'group_by' : 'loan_issue_id'}"/> |
|||
<filter name="partner_id" string="Loaner" |
|||
context="{'group_by' : 'partner_id'}"/> |
|||
<filter name="issue_line" string="Loans" |
|||
context="{'group_by' : 'issue_line'}"/> |
|||
<filter name="due_date" string="Due Date" |
|||
context="{'group_by': 'due_date'}"/> |
|||
<filter name="state" string="State" |
|||
context="{'group_by': 'state'}"/> |
|||
</group> |
|||
</search> |
|||
</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,114 @@ |
|||
<odoo> |
|||
<record id="action_loan_issue_lines" model="ir.actions.act_window"> |
|||
<field name="name">Loans</field> |
|||
<field name="res_model">loan.issue.line</field> |
|||
<field name="view_type">form</field> |
|||
<field name="view_mode">tree,form</field> |
|||
</record> |
|||
|
|||
<record id="loan_issue_line_view_tree" model="ir.ui.view"> |
|||
<field name="name">loan_issue_line_view_tree</field> |
|||
<field name="model">loan.issue.line</field> |
|||
<field name="arch" type="xml"> |
|||
<tree string="Loans"> |
|||
<field name="name"/> |
|||
<field name="partner_id"/> |
|||
<field name="loan_issue_id"/> |
|||
<field name="quantity"/> |
|||
<field name="face_value"/> |
|||
<field name="amount"/> |
|||
<field name="date"/> |
|||
<field name="state"/> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="view_loan_issue_line_form" model="ir.ui.view"> |
|||
<field name="name">loan.issue.line.form</field> |
|||
<field name="model">loan.issue.line</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Loan"> |
|||
<header> |
|||
<button name="action_validate" string="Validate" |
|||
type="object" states="draft"/> |
|||
<button name="action_request_payment" |
|||
string="Request Payment" |
|||
type="object" states="subscribed"/> |
|||
<button name="action_draft" string="Set to draft" |
|||
type="object" states="cancelled"/> |
|||
<button name="action_paid" string="Paid" |
|||
type="object" states="waiting"/> |
|||
<button name="action_cancel" string="Cancel" type="object" |
|||
states="draft,subscribed,waiting" |
|||
confirm="Are you sure you want to cancel this loan subscription ?"/> |
|||
<button name="action_compute_interest" string="Compute interest" |
|||
type="object" states="paid"/> |
|||
|
|||
<field name="state" widget="statusbar"/> |
|||
</header> |
|||
<sheet> |
|||
<group> |
|||
<group> |
|||
<field name="name"/> |
|||
<field name="partner_id"/> |
|||
<field name="loan_issue_id"/> |
|||
<field name="quantity"/> |
|||
<field name="face_value"/> |
|||
</group> |
|||
<group> |
|||
<field name="date"/> |
|||
<field name="payment_date"/> |
|||
<field name="reference"/> |
|||
<field name="amount"/> |
|||
</group> |
|||
</group> |
|||
<notebook> |
|||
<page name="interest_lines" string="Interest lines"> |
|||
<field name="interest_lines"> |
|||
<tree delete="false" create="false"> |
|||
<field name="name"/> |
|||
<field name="amount"/> |
|||
<field name="accrued_amount"/> |
|||
<field name="interest"/> |
|||
<field name="accrued_interest"/> |
|||
<field name="net_interest"/> |
|||
<field name="taxes_amount"/> |
|||
<field name="due_amount"/> |
|||
<field name="due_date"/> |
|||
<field name="state"/> |
|||
</tree> |
|||
</field> |
|||
</page> |
|||
</notebook> |
|||
</sheet> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="view_loan_issue_line_filter" model="ir.ui.view"> |
|||
<field name="name">Loans Search</field> |
|||
<field name="model">loan.issue.line</field> |
|||
<field name="arch" type="xml"> |
|||
<search string="Search Loans"> |
|||
<field name="name"/> |
|||
<field name="partner_id"/> |
|||
<field name="loan_issue_id"/> |
|||
<separator/> |
|||
<filter string="Draft" name="state_draft" |
|||
domain="[('state','=','draft')]"/> |
|||
<filter string="Paid" name="state_paid" |
|||
domain="[('state','=','paid')]"/> |
|||
<filter string="Done" name="state_done" |
|||
domain="[('state','=','done')]"/> |
|||
<filter string="Subscribed" name="state_subscribed" |
|||
domain="[('state','=','subscribed')]"/> |
|||
<group expand="0" name="group_by" string="Group By"> |
|||
<filter name="loan_issue_id" string="Loan Issue" |
|||
context="{'group_by' : 'loan_issue_id'}"/> |
|||
<filter name="date" string="Subscription Date" |
|||
context="{'group_by': 'date'}"/> |
|||
</group> |
|||
</search> |
|||
</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,2 @@ |
|||
from . import models |
|||
from . import wizard |
@ -0,0 +1,29 @@ |
|||
# Copyright 2020 - ongoing Coop IT Easy SCRLfs (<http://www.coopiteasy.be>) |
|||
# - Houssine BAKKALI - <houssine@coopiteasy.be> |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). |
|||
|
|||
{ |
|||
"name": "Easy My Coop Loan Account", |
|||
"version": "12.0.1.0.0", |
|||
"depends": [ |
|||
"account", |
|||
"account_fiscal_year", |
|||
"easy_my_coop_loan", |
|||
], |
|||
"author": "Coop IT Easy SCRLfs", |
|||
"category": "Cooperative management", |
|||
"website": "https://www.coopiteasy.be", |
|||
"license": "AGPL-3", |
|||
"summary": """ |
|||
This module brings the accounting part of the loan issue. |
|||
It has for purpose to generate all the accounting entries to the covered |
|||
use cases. |
|||
""", |
|||
"data": [ |
|||
"data/emc_loan_account_data.xml", |
|||
"views/res_company_view.xml", |
|||
"views/loan_issue_line_view.xml", |
|||
"wizard/end_of_year_operation.xml", |
|||
], |
|||
"installable": True, |
|||
} |
@ -0,0 +1,31 @@ |
|||
<odoo> |
|||
<data noupdate="1"> |
|||
<record id="sequence_awaiting_loan_payment_journal" model="ir.sequence"> |
|||
<field name="name">Awaiting Loan Payment Journal</field> |
|||
<field eval="3" name="padding"/> |
|||
<field name="prefix">ALPJ/%(year)s/</field> |
|||
<field name="use_date_range">True</field> |
|||
</record> |
|||
|
|||
<record id="subscription_journal" model="account.journal"> |
|||
<field name="name">Awaiting Loan Payment Journal</field> |
|||
<field name="code">ALPJ</field> |
|||
<field name="type">general</field> |
|||
<field name="sequence_id" ref="sequence_awaiting_loan_payment_journal"/> |
|||
</record> |
|||
|
|||
<record id="sequence_loan_journal" model="ir.sequence"> |
|||
<field name="name">Loan Journal</field> |
|||
<field eval="3" name="padding"/> |
|||
<field name="prefix">LOANJ/%(year)s/</field> |
|||
<field name="use_date_range">True</field> |
|||
</record> |
|||
|
|||
<record id="loan_journal" model="account.journal"> |
|||
<field name="name">Loan Journal</field> |
|||
<field name="code">LOANJ</field> |
|||
<field name="type">general</field> |
|||
<field name="sequence_id" ref="sequence_loan_journal"/> |
|||
</record> |
|||
</data> |
|||
</odoo> |
@ -0,0 +1,5 @@ |
|||
from . import company |
|||
from . import loan_issue_line |
|||
from . import interest_line |
|||
from . import account_move |
|||
from . import account_fiscal_year |
@ -0,0 +1,46 @@ |
|||
# Copyright 2020 Coop IT Easy SCRL fs |
|||
# Houssine BAKKALI <houssine@coopiteasy.be> |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). |
|||
|
|||
|
|||
from dateutil.relativedelta import relativedelta |
|||
|
|||
from odoo import api, fields, models |
|||
from odoo.exceptions import UserError |
|||
|
|||
|
|||
class AccountFiscalYear(models.Model): |
|||
_inherit = "account.fiscal.year" |
|||
|
|||
@api.model |
|||
def get_ongoing_fiscal_year(self, company_id=None): |
|||
today = fields.Date.today() |
|||
fy = self.env["account.fiscal.year"].search([ |
|||
('date_from', '<=', today), |
|||
('date_to', '>=', today), |
|||
]) |
|||
|
|||
if not fy: |
|||
raise UserError("No fiscal year has been found for %s" % |
|||
str(today)) |
|||
|
|||
if company_id: |
|||
return fy.filtered(lambda r: r.company_id == company_id) |
|||
return fy |
|||
|
|||
@api.model |
|||
def get_next_fiscal_year(self, date=None, company_id=None): |
|||
if not date: |
|||
date = fields.Date.today() |
|||
nextyear = date + relativedelta(years=+1) |
|||
fy = self.env["account.fiscal.year"].search([ |
|||
('date_from', '<=', nextyear), |
|||
('date_to', '>=', nextyear), |
|||
]) |
|||
if not fy: |
|||
raise UserError("No next fiscal year has been found for %s" % |
|||
str(nextyear)) |
|||
if company_id: |
|||
return fy.filtered(lambda r: r.company_id == company_id) |
|||
|
|||
return fy |
@ -0,0 +1,39 @@ |
|||
# Copyright 2020 Coop IT Easy SCRL fs |
|||
# Houssine BAKKALI <houssine@coopiteasy.be> |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class AccountMove(models.Model): |
|||
_inherit = "account.move" |
|||
|
|||
loan_issue_line = fields.One2many( |
|||
"loan.issue.line", |
|||
"awaiting_move_id", |
|||
string="Loan issue line", |
|||
readonly=True, |
|||
) |
|||
|
|||
|
|||
class AccountMoveLine(models.Model): |
|||
_inherit = "account.move.line" |
|||
|
|||
loan_issue_line = fields.One2many( |
|||
"loan.issue.line", |
|||
related="move_id.loan_issue_line", |
|||
) |
|||
|
|||
@api.multi |
|||
def check_full_reconcile(self): |
|||
super(AccountMoveLine, self).check_full_reconcile() |
|||
full_reconcile_id = self.mapped("full_reconcile_id") |
|||
loan_issue_line = self.mapped("loan_issue_line") |
|||
if full_reconcile_id and loan_issue_line: |
|||
for move_line in self: |
|||
if move_line.statement_id: |
|||
loan_issue_line.payment_date = move_line.date |
|||
loan_issue_line.with_context( |
|||
{"paid_by_bank_statement": True} |
|||
).action_paid() |
|||
print(full_reconcile_id) |
@ -0,0 +1,92 @@ |
|||
# Copyright 2020 Coop IT Easy SCRL fs |
|||
# Houssine Bakkali <houssine@coopiteasy.be> |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). |
|||
|
|||
|
|||
from odoo import fields, models |
|||
|
|||
|
|||
class ResCompany(models.Model): |
|||
_inherit = "res.company" |
|||
|
|||
awaiting_loan_payment_account = fields.Many2one( |
|||
"account.account", |
|||
company_dependent=True, |
|||
string="Awaiting Loan Payment Account", |
|||
domain=[ |
|||
("internal_type", "=", "receivable"), |
|||
("deprecated", "=", False), |
|||
], |
|||
help="This account serve to track awaiting payment." |
|||
" It only serve a bank reconciliation purpose to register the awaiting" |
|||
" loan payment as received/paid", |
|||
required=True, |
|||
) |
|||
loaner_account = fields.Many2one( |
|||
"account.account", |
|||
company_dependent=True, |
|||
string="Loaner Account", |
|||
help="This account will be the default one as the" |
|||
" receivable account for the cooperators", |
|||
required=True, |
|||
) |
|||
debt_long_term_account = fields.Many2one( |
|||
"account.account", |
|||
company_dependent=True, |
|||
string="Long Term Debt Account", |
|||
help="This account is used to register the loan debt due for more" |
|||
" than one year", |
|||
required=True, |
|||
) |
|||
debt_long_term_fy_account = fields.Many2one( |
|||
"account.account", |
|||
company_dependent=True, |
|||
string="Long Term Debt Due In The Year Account", |
|||
help="This account is used to register the loan debt due for the" |
|||
" current fiscal year", |
|||
required=True, |
|||
) |
|||
debt_long_term_due_account = fields.Many2one( |
|||
"account.account", |
|||
company_dependent=True, |
|||
string="Long Term Debt Due Account", |
|||
help="This account is used to register the loan debt due", |
|||
required=True, |
|||
) |
|||
expense_account = fields.Many2one( |
|||
"account.account", |
|||
company_dependent=True, |
|||
string="Expense Account", |
|||
help="This account is used to register the prorata temporis interest" |
|||
" amount at the end of the fiscal year", |
|||
required=True, |
|||
) |
|||
interest_account = fields.Many2one( |
|||
"account.account", |
|||
company_dependent=True, |
|||
string="Interest Account", |
|||
help="This account is used to register the due loan interest", |
|||
required=True, |
|||
) |
|||
tax_account = fields.Many2one( |
|||
"account.account", |
|||
company_dependent=True, |
|||
string="Tax Account", |
|||
help="This account is used to register the tax to pay" |
|||
" to the tax administration", |
|||
required=True, |
|||
) |
|||
awaiting_loan_payment_journal = fields.Many2one( |
|||
"account.journal", |
|||
string="Awaiting loan payment journal", |
|||
help="This journal will be the default one as the" |
|||
" to track the payment from the loaners", |
|||
required=True, |
|||
) |
|||
loan_journal = fields.Many2one( |
|||
"account.journal", |
|||
string="Loan journal", |
|||
help="This journal will be the one used to register all" |
|||
" the loan account move lines", |
|||
required=True, |
|||
) |
@ -0,0 +1,200 @@ |
|||
# Copyright 2020 Coop IT Easy SCRL fs |
|||
# Houssine BAKKALI <houssine@coopiteasy.be> |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). |
|||
from dateutil.relativedelta import relativedelta |
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class LoanInterestLine(models.Model): |
|||
_inherit = "loan.interest.line" |
|||
|
|||
loan_due_fy_move = fields.Many2one( |
|||
comodel_name="account.move", |
|||
string="Loan due this fiscal year account move" |
|||
) |
|||
loan_due_move = fields.Many2one( |
|||
comodel_name="account.move", |
|||
string="Loan due this fiscal year account move" |
|||
) |
|||
loan_reimbursment_move = fields.Many2one( |
|||
comodel_name="account.move", |
|||
string="Loan reimbursement account move" |
|||
) |
|||
interest_closing_fy = fields.Many2one( |
|||
comodel_name="account.move", |
|||
string="Interest closing fiscal year account move" |
|||
) |
|||
interest_opening_fy = fields.Many2one( |
|||
comodel_name="account.move", |
|||
string="Interest opening fiscal year account move" |
|||
) |
|||
|
|||
@api.multi |
|||
def get_move_line(self, move_id, partner=None): |
|||
self.ensure_one() |
|||
move_line = { |
|||
"date_maturity": self.due_date, |
|||
"date": self.due_date, |
|||
"move_id": move_id.id, |
|||
} |
|||
if partner: |
|||
move_line["partner_id"] = partner.id |
|||
return move_line |
|||
|
|||
@api.multi |
|||
def create_move(self, date=None): |
|||
self.ensure_one() |
|||
if date: |
|||
due_date = date |
|||
else: |
|||
due_date = self.due_date |
|||
|
|||
return self.env["account.move"].create({ |
|||
"ref": self.name, |
|||
"date": due_date, |
|||
"journal_id": self.company_id.loan_journal.id, |
|||
}) |
|||
|
|||
@api.multi |
|||
def generate_payment_move_lines(self): |
|||
|
|||
for line in self: |
|||
if not self.loan_reimbursment_move: |
|||
company = line.company_id |
|||
move = line.create_move() |
|||
|
|||
debit_vals = line.get_move_line(move, line.partner_id) |
|||
loaner_vals = line.get_move_line(move, line.partner_id) |
|||
|
|||
debit_vals["debit"] = line.interest |
|||
debit_vals["account_id"] = company.interest_account.id |
|||
|
|||
if line.due_loan_amount > 0 and line.net_interest > 0: |
|||
loaner_vals["credit"] = line.due_amount |
|||
elif line.due_loan_amount > 0: |
|||
loaner_vals["credit"] = line.due_loan_amount |
|||
elif line.net_interest > 0: |
|||
loaner_vals["credit"] = line.net_interest |
|||
loaner_vals["account_id"] = company.loaner_account |
|||
|
|||
vals_list = [debit_vals, loaner_vals] |
|||
|
|||
if line.taxes_amount > 0: |
|||
tax_vals = line.get_move_line(move) |
|||
tax_vals["credit"] = line.taxes_amount |
|||
tax_vals["account_id"] = company.tax_account.id |
|||
vals_list.append(tax_vals) |
|||
|
|||
self.env["account.move.line"].create(vals_list) |
|||
|
|||
line.write({"loan_reimbursment_move": move.id}) |
|||
|
|||
@api.multi |
|||
def generate_interest_move_lines_fy(self, date, next_fy): |
|||
aml_obj = self.env["account.move.line"] |
|||
for line in self: |
|||
if not self.interest_closing_fy: |
|||
company = line.company_id |
|||
|
|||
# compute the prorata interest for the fiscal year |
|||
prorata_date = line.due_date - relativedelta(years=-1) |
|||
diff_days = (prorata_date - date).days |
|||
days = line.issue_line.get_number_of_days(date.year) |
|||
|
|||
previous_interest = line.accrued_interest - line.interest |
|||
prorata_interest = line.interest * (diff_days / days) |
|||
interest_fy = previous_interest + prorata_interest |
|||
|
|||
# create interest closing account move lines |
|||
close_fy_move = line.create_move(date) |
|||
deb_vals = line.get_move_line(close_fy_move, line.partner_id) |
|||
cred_vals = line.get_move_line(close_fy_move, line.partner_id) |
|||
|
|||
deb_vals["debit"] = interest_fy |
|||
deb_vals["date"] = date |
|||
deb_vals["account_id"] = company.interest_account.id |
|||
|
|||
cred_vals["credit"] = interest_fy |
|||
cred_vals["date"] = date |
|||
cred_vals["account_id"] = company.expense_account.id |
|||
|
|||
aml_obj.create([deb_vals, cred_vals]) |
|||
|
|||
line.write({"interest_closing_fy": close_fy_move.id}) |
|||
|
|||
# create interest opening account move lines |
|||
opening_date = next_fy.date_from |
|||
open_fy_move = line.create_move(opening_date) |
|||
deb_vals = line.get_move_line(open_fy_move, line.partner_id) |
|||
cred_vals = line.get_move_line(open_fy_move, line.partner_id) |
|||
|
|||
deb_vals["debit"] = interest_fy |
|||
deb_vals["date"] = opening_date |
|||
deb_vals["account_id"] = company.expense_account.id |
|||
|
|||
cred_vals["credit"] = interest_fy |
|||
cred_vals["date"] = opening_date |
|||
cred_vals["account_id"] = company.interest_account.id |
|||
|
|||
aml_obj.create([deb_vals, cred_vals]) |
|||
line.write({"interest_opening_fy": open_fy_move.id}) |
|||
|
|||
@api.multi |
|||
def generate_loan_due_fy(self, date): |
|||
|
|||
for line in self: |
|||
if not self.loan_due_fy_move: |
|||
company = line.company_id |
|||
move = line.create_move(date) |
|||
|
|||
deb_vals = line.get_move_line(move, line.partner_id) |
|||
cred_vals = line.get_move_line(move, line.partner_id) |
|||
|
|||
deb_vals["debit"] = line.due_loan_amount |
|||
deb_vals["date"] = date |
|||
deb_vals["account_id"] = company.debt_long_term_account.id |
|||
|
|||
cred_vals["credit"] = line.due_loan_amount |
|||
cred_vals["date"] = date |
|||
cred_vals["account_id"] = company.debt_long_term_fy_account.id |
|||
|
|||
self.env["account.move.line"].create([deb_vals, cred_vals]) |
|||
|
|||
line.write({"loan_due_fy_move": move.id, |
|||
"state": "due_fy"}) |
|||
|
|||
@api.multi |
|||
def generate_loan_due_now(self): |
|||
|
|||
for line in self: |
|||
if line.loan_due_move: |
|||
company = line.company_id |
|||
move = line.create_move(fields.Date.today()) |
|||
|
|||
debit_vals = line.get_move_line(move, line.partner_id) |
|||
credit_vals = line.get_move_line(move, line.partner_id) |
|||
|
|||
debit_vals["debit"] = line.due_loan_amount |
|||
debit_vals["account_id"] = company.debt_long_term_fy_account.id |
|||
|
|||
credit_vals["credit"] = line.due_loan_amount |
|||
credit_vals["account_id"] = company.debt_long_term_due_account |
|||
|
|||
self.env["account.move.line"].create([debit_vals, credit_vals]) |
|||
|
|||
line.write({"loan_due_move": move.id, |
|||
"state": "due"}) |
|||
|
|||
@api.model |
|||
def _generate_payment_move(self): |
|||
# TODO configure how many days before you want generate the move lines |
|||
fy = self.env["account.fiscal.year"].get_next_fiscal_year() |
|||
interest_lines = self.search([ |
|||
('due_date', '>=', fy.date_from), |
|||
('due_date', '<=', fy.date_to), |
|||
('due_amount', '>', 0), |
|||
]) |
|||
|
|||
interest_lines.generate_payment_move_lines() |
|||
|
|||
return True |
@ -0,0 +1,71 @@ |
|||
# Copyright 2020 Coop IT Easy SCRL fs |
|||
# Houssine BAKKALI <houssine@coopiteasy.be> |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from datetime import date |
|||
|
|||
from odoo import api, fields, models, _ |
|||
from odoo.exceptions import UserError |
|||
|
|||
|
|||
class LoanIssueLine(models.Model): |
|||
_inherit = "loan.issue.line" |
|||
|
|||
awaiting_move_id = fields.Many2one( |
|||
comodel_name="account.move", |
|||
string="Awaiting payment account move" |
|||
) |
|||
|
|||
@api.multi |
|||
def get_loan_move_line(self, move_id): |
|||
self.ensure_one() |
|||
move_line = { |
|||
"partner_id": self.partner_id.id, |
|||
"date_maturity": date.today(), |
|||
"move_id": move_id, |
|||
} |
|||
return move_line |
|||
|
|||
@api.model |
|||
def create_move(self, line, date, journal): |
|||
return self.env["account.move"].create({ |
|||
"ref": line.reference, |
|||
"date": date.today(), |
|||
"journal_id": journal, |
|||
}) |
|||
|
|||
@api.multi |
|||
def create_waiting_payment_move(self): |
|||
move_line_obj = self.env["account.move.line"] |
|||
for line in self: |
|||
comp = line.company_id |
|||
move = self.create_move( |
|||
line, |
|||
date.today(), |
|||
comp.awaiting_loan_payment_journal.id, |
|||
) |
|||
loan_vals = line.get_loan_move_line(move.id) |
|||
loaner_vals = line.get_loan_move_line(move.id) |
|||
|
|||
loan_vals["account_id"] = comp.debt_long_term_account.id |
|||
loan_vals["credit"] = line.amount |
|||
|
|||
loaner_vals["account_id"] = comp.awaiting_loan_payment_account.id |
|||
loaner_vals["debit"] = line.amount |
|||
|
|||
move_line_obj.create([loan_vals, loaner_vals]) |
|||
line.awaiting_move_id = move |
|||
|
|||
@api.multi |
|||
def action_request_payment(self): |
|||
self.create_waiting_payment_move() |
|||
super(LoanIssueLine, self).action_request_payment() |
|||
|
|||
@api.multi |
|||
def action_paid(self): |
|||
paid_by = self.env.context.get("paid_by_bank_statement") |
|||
if paid_by: |
|||
super(LoanIssueLine, self).action_paid() |
|||
else: |
|||
raise UserError(_("The payment must be registered" |
|||
" by bank statement")) |
@ -0,0 +1,22 @@ |
|||
<odoo> |
|||
|
|||
<record id="view_loan_issue_line_account_form" model="ir.ui.view"> |
|||
<field name="name">loan.issue.line.form</field> |
|||
<field name="model">loan.issue.line</field> |
|||
<field name="inherit_id" ref="easy_my_coop_loan.view_loan_issue_line_form"/> |
|||
<field name="arch" type="xml"> |
|||
<page name="interest_lines" position="after"> |
|||
<page name="accounting" string="Accounting"> |
|||
<group> |
|||
<group> |
|||
<field name="awaiting_move_id" readonly="True"/> |
|||
</group> |
|||
<group> |
|||
</group> |
|||
</group> |
|||
</page> |
|||
</page> |
|||
</field> |
|||
</record> |
|||
|
|||
</odoo> |
@ -0,0 +1,25 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<odoo> |
|||
<record model="ir.ui.view" id="view_company_easy_my_coop_loan"> |
|||
<field name="name">res.company.form.easymy.coop</field> |
|||
<field name="inherit_id" ref="easy_my_coop.view_company_inherit_form2"/> |
|||
<field name="model">res.company</field> |
|||
<field name="arch" type="xml"> |
|||
<group name="coop_grp" position="before"> |
|||
<group name="coop_loan_grp" string="EasyMy Coop Loan" |
|||
groups="easy_my_coop.group_easy_my_coop_user"> |
|||
<field name="awaiting_loan_payment_account"/> |
|||
<field name="loaner_account"/> |
|||
<field name="debt_long_term_account"/> |
|||
<field name="debt_long_term_fy_account"/> |
|||
<field name="debt_long_term_due_account"/> |
|||
<field name="expense_account"/> |
|||
<field name="interest_account"/> |
|||
<field name="tax_account"/> |
|||
<field name="awaiting_loan_payment_journal"/> |
|||
<field name="loan_journal"/> |
|||
</group> |
|||
</group> |
|||
</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1 @@ |
|||
from . import end_of_year_operation |
@ -0,0 +1,77 @@ |
|||
# Copyright 2020 Coop IT Easy SCRL fs |
|||
# Houssine BAKKALI <houssine@coopiteasy.be> |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). |
|||
from odoo import api, fields, models |
|||
|
|||
from odoo.exceptions import UserError |
|||
|
|||
|
|||
class LoanEndOfYearOperation(models.TransientModel): |
|||
_name = "loan.end.of.year.operation" |
|||
operation_type = fields.Selection( |
|||
[("eoy_operation", "End of year operation"), |
|||
("loan_due", "Loan payment account move lines")], |
|||
required=True, |
|||
string="Operation type" |
|||
) |
|||
ongoing_fy_id = fields.Many2one( |
|||
comodel_name="account.fiscal.year", |
|||
string="Ongoing fiscal year", |
|||
required=True |
|||
) |
|||
due_date = fields.Date( |
|||
string="Due date" |
|||
) |
|||
|
|||
@api.multi |
|||
def run(self): |
|||
self.ensure_one() |
|||
afy_obj = self.env["account.fiscal.year"] |
|||
interest_line_obj = self.env["loan.interest.line"] |
|||
|
|||
loan_issues = self.env["loan.issue"].browse( |
|||
self._context.get("active_ids") |
|||
) |
|||
|
|||
last_fy_day = self.ongoing_fy_id.date_to |
|||
next_fy = afy_obj.get_next_fiscal_year(last_fy_day) |
|||
|
|||
if self.ongoing_fy_id == "eoy_operation": |
|||
if next_fy: |
|||
interest_lines_loan = interest_line_obj.search([ |
|||
("due_date", ">=", next_fy.date_from), |
|||
("due_date", "<=", next_fy.date_to), |
|||
("due_loan_amount", ">", 0), |
|||
("loan_issue_id", "in", loan_issues.ids), |
|||
("loan_reimbursment_move", "=", False) |
|||
]) |
|||
|
|||
interest_lines_loan.generate_loan_due_fy(last_fy_day) |
|||
interest_lines = interest_line_obj.search([ |
|||
("due_date", ">=", next_fy.date_from), |
|||
("due_date", "<=", next_fy.date_to), |
|||
("interest", ">", 0), |
|||
("loan_issue_id", "in", loan_issues.ids), |
|||
("interest_closing_fy", "=", False), |
|||
("interest_opening_fy", "=", False) |
|||
]) |
|||
|
|||
interest_lines.generate_interest_move_lines_fy(last_fy_day, |
|||
next_fy) |
|||
# interest_lines.write({"state": "scheduled"}) |
|||
# interest_lines_loan.write({"state": "scheduled"}) |
|||
(interest_lines + interest_lines_loan).write({"state": |
|||
"scheduled"}) |
|||
if not interest_lines_loan and not interest_lines: |
|||
raise UserError("There is no end of year account move" |
|||
" lines to generate for the selected loan" |
|||
" issue") |
|||
elif self.ongoing_fy_id == "loan_due": |
|||
interest_lines = interest_line_obj.search([ |
|||
("due_date", ">=", next_fy.date_from), |
|||
("due_date", "<=", next_fy.date_to), |
|||
("interest", ">", 0), |
|||
("loan_issue_id", "in", loan_issues.ids), |
|||
("interest_closing_fy", "=", False), |
|||
("interest_opening_fy", "=", False) |
|||
]) |
@ -0,0 +1,35 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<record id="view_loan_end_of_year_operation" model="ir.ui.view"> |
|||
<field name="name">End of year loan operation</field> |
|||
<field name="model">loan.end.of.year.operation</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="End of year loan operation"> |
|||
<p class="oe_grey"> |
|||
End of year loan operation. |
|||
</p> |
|||
<group> |
|||
<field name="ongoing_fy_id"/> |
|||
<field name="due_date" attrs="{'invisible':[('ongoing_fy_id', '!=', 'loan_due')]}"/> |
|||
</group> |
|||
<footer> |
|||
<button name="run" string="Run" type="object" |
|||
class="btn-primary"/> |
|||
<button string="Cancel" class="btn-default" |
|||
special="cancel"/> |
|||
</footer> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<act_window id="action_view_loan_end_of_year_operation" |
|||
multi="True" |
|||
key2="client_action_multi" |
|||
name="End of year loan operation" |
|||
res_model="loan.end.of.year.operation" |
|||
src_model="loan.issue" |
|||
view_mode="form" |
|||
view_type="form" |
|||
target="new" |
|||
groups="easy_my_coop.group_easy_my_coop_manager"/> |
|||
</odoo> |
@ -0,0 +1 @@ |
|||
from . import models |
@ -0,0 +1,21 @@ |
|||
# Copyright 2020 - ongoing Coop IT Easy (http://www.coopiteasy.be) |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
{ |
|||
'name': 'Easy My Coop Loan BBA', |
|||
'category': 'Sales', |
|||
'author': "Coop IT Easy - Houssine BAKKALI <houssine@coopiteasy.be>", |
|||
'website': 'https://www.coopiteasy.be', |
|||
'version': '12.0.1.0.0', |
|||
'license': 'AGPL-3', |
|||
'depends': [ |
|||
'easy_my_coop_loan', |
|||
'l10n_be_invoice_bba' |
|||
], |
|||
"description": """ |
|||
This module implements the bba structured communication on the loan line. |
|||
""", |
|||
'data': [ |
|||
], |
|||
'installable': True, |
|||
} |
@ -0,0 +1 @@ |
|||
from . import loan_issue_line |
@ -0,0 +1,95 @@ |
|||
# © 2018 Coop IT Easy (http://www.coopiteasy.be) |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
import random |
|||
import re |
|||
|
|||
from odoo import api, fields, models, _ |
|||
from odoo.exceptions import UserError |
|||
|
|||
|
|||
class LoanIssueLine(models.Model): |
|||
|
|||
_inherit = 'loan.issue.line' |
|||
|
|||
@api.multi |
|||
def generate_bbacomm(self): |
|||
self.ensure_one() |
|||
algorithm = self.company_id.l10n_be_structured_comm |
|||
if algorithm == 'date': |
|||
date = fields.Date.from_string(fields.Date.today()) |
|||
doy = date.strftime('%j') |
|||
year = date.strftime('%Y') |
|||
seq = '001' |
|||
invoices = self.search([('type', '=', 'out_invoice'), |
|||
('reference', 'like', '+++%s/%s/%%' % |
|||
(doy, year))], order='reference') |
|||
if invoices: |
|||
prev_seq = int(invoices[-1].reference[12:15]) |
|||
if prev_seq < 999: |
|||
seq = '%03d' % (prev_seq + 1) |
|||
else: |
|||
raise UserError(_('The daily maximum of outgoing invoices ' |
|||
'with an automatically generated BBA ' |
|||
'Structured Communications has been ' |
|||
'exceeded!' '\nPlease create manually a ' |
|||
'unique BBA Structured Communication.')) |
|||
bbacomm = doy + year + seq |
|||
base = int(bbacomm) |
|||
mod = base % 97 or 97 |
|||
reference = '+++%s/%s/%s%02d+++' % (doy, year, seq, mod) |
|||
elif algorithm == 'partner_ref': |
|||
partner_ref = self.partner_id.ref |
|||
print(partner_ref) |
|||
partner_ref_nr = re.sub('\D', '', partner_ref or '') |
|||
if (len(partner_ref_nr) < 3) or (len(partner_ref_nr) > 7): |
|||
raise UserError(_('The Customer should have an Internal ' |
|||
'Reference with min 3 and max 7 digits ' |
|||
'for the generation of BBA Structured ' |
|||
'Communications!')) |
|||
else: |
|||
partner_ref_nr = partner_ref_nr.ljust(7, '0') |
|||
seq = '001' |
|||
invoices = self.search([('type', '=', 'out_invoice'), |
|||
('reference', 'like', '+++%s/%s/%%' % |
|||
(partner_ref_nr[:3], |
|||
partner_ref_nr[3:])) |
|||
], order='reference') |
|||
if invoices: |
|||
prev_seq = int(invoices[-1].reference[12:15]) |
|||
if prev_seq < 999: |
|||
seq = '%03d' % (prev_seq + 1) |
|||
else: |
|||
raise UserError(_( |
|||
'The daily maximum of outgoing invoices with an ' |
|||
'automatically generated BBA Structured ' |
|||
'Communications has been exceeded!' |
|||
'\nPlease create manually a unique BBA Structured ' |
|||
'Communication.')) |
|||
bbacomm = partner_ref_nr + seq |
|||
base = int(bbacomm) |
|||
mod = base % 97 or 97 |
|||
reference = '+++%s/%s/%s%02d+++' % (partner_ref_nr[:3], |
|||
partner_ref_nr[3:], seq, mod) |
|||
elif algorithm == 'random': |
|||
base = random.randint(1, 9999999999) |
|||
bbacomm = str(base).rjust(10, '0') |
|||
base = int(bbacomm) |
|||
mod = base % 97 or 97 |
|||
mod = str(mod).rjust(2, '0') |
|||
reference = '+++%s/%s/%s%s+++' % (bbacomm[:3], |
|||
bbacomm[3:7], |
|||
bbacomm[7:], mod) |
|||
else: |
|||
raise UserError(_("Unsupported Structured Communication Type " |
|||
"Algorithm '%s' !" |
|||
"\nPlease contact your Odoo support channel." |
|||
) % algorithm) |
|||
return reference |
|||
|
|||
@api.multi |
|||
def action_validate(self): |
|||
super(LoanIssueLine, self).action_validate() |
|||
for line in self: |
|||
bba = line.generate_bbacomm() |
|||
line.write({'reference': bba}) |
Write
Preview
Loading…
Cancel
Save
Reference in new issue