Browse Source

Merge 296c4e683b into 0b4aef30c6

pull/134/merge
Houssine BAKKALI 4 years ago
committed by GitHub
parent
commit
39083bac7e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 15
      easy_my_coop_loan/__manifest__.py
  2. 12
      easy_my_coop_loan/data/loan_data.xml
  3. 8
      easy_my_coop_loan/data/mail_template_data.xml
  4. 33
      easy_my_coop_loan/models/interest_line.py
  5. 73
      easy_my_coop_loan/models/loan.py
  6. 148
      easy_my_coop_loan/models/loan_issue_line.py
  7. 111
      easy_my_coop_loan/report/loan_issue_line_report.xml
  8. 13
      easy_my_coop_loan/report/loan_report.xml
  9. 2
      easy_my_coop_loan/security/ir.model.access.csv
  10. 96
      easy_my_coop_loan/views/loan_interest_lines_view.xml
  11. 114
      easy_my_coop_loan/views/loan_line_view.xml
  12. 111
      easy_my_coop_loan/views/loan_view.xml
  13. 2
      easy_my_coop_loan_account/__init__.py
  14. 29
      easy_my_coop_loan_account/__manifest__.py
  15. 31
      easy_my_coop_loan_account/data/emc_loan_account_data.xml
  16. 5
      easy_my_coop_loan_account/models/__init__.py
  17. 46
      easy_my_coop_loan_account/models/account_fiscal_year.py
  18. 39
      easy_my_coop_loan_account/models/account_move.py
  19. 92
      easy_my_coop_loan_account/models/company.py
  20. 200
      easy_my_coop_loan_account/models/interest_line.py
  21. 71
      easy_my_coop_loan_account/models/loan_issue_line.py
  22. 22
      easy_my_coop_loan_account/views/loan_issue_line_view.xml
  23. 25
      easy_my_coop_loan_account/views/res_company_view.xml
  24. 1
      easy_my_coop_loan_account/wizard/__init__.py
  25. 77
      easy_my_coop_loan_account/wizard/end_of_year_operation.py
  26. 35
      easy_my_coop_loan_account/wizard/end_of_year_operation.xml
  27. 1
      easy_my_coop_loan_bba/__init__.py
  28. 21
      easy_my_coop_loan_bba/__manifest__.py
  29. 1
      easy_my_coop_loan_bba/models/__init__.py
  30. 95
      easy_my_coop_loan_bba/models/loan_issue_line.py
  31. 11
      easy_my_coop_loan_website/__manifest__.py
  32. 26
      easy_my_coop_loan_website/controllers/main.py
  33. 6
      easy_my_coop_website/__manifest__.py
  34. 9
      easy_my_coop_website_portal/__manifest__.py
  35. 6
      easy_my_coop_website_portal/controllers/main.py
  36. 14
      easy_my_coop_website_portal/views/easy_my_coop_website_portal_templates.xml

15
easy_my_coop_loan/__manifest__.py

@ -3,12 +3,12 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Easy My Coop Bond and Subordinated Loan Issues",
"version": "12.0.1.0.1",
"name": "Easy My Coop Loan Issues Management",
"version": "12.0.2.0.1",
"depends": ["easy_my_coop"],
"author": "Coop IT Easy SCRLfs",
"category": "Cooperative management",
"website": "http://www.coopiteasy.be",
"website": "https://www.coopiteasy.be",
"license": "AGPL-3",
"summary": """
This module allows to manage the bonds and
@ -17,10 +17,17 @@
"data": [
"security/ir.model.access.csv",
"views/loan_view.xml",
"views/loan_line_view.xml",
"views/loan_interest_lines_view.xml",
"views/partner_view.xml",
"views/menus.xml",
"report/loan_issue_line_report.xml",
"report/loan_report.xml",
"data/mail_template_data.xml",
"data/loan_data.xml"
],
"demo": [
"demo/coop.xml"
],
"demo": ["demo/coop.xml"],
"installable": True,
}

12
easy_my_coop_loan/data/loan_data.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>

8
easy_my_coop_loan/data/mail_template_data.xml

@ -92,7 +92,7 @@
<p>Amount: ${object.amount} ${object.loan_issue_id.company_currency_id.symbol}</p>
<p>Account number: ${object.company_id.bank_ids[0].sanitized_acc_number}</p>
<p>Communication: ${object.loan_issue_id.name} + ${object.partner_id.name}</p>
<p>Communication: ${object.reference}</p>
<p>Sustainably your,</p>
<p>${object.company_id.name}.</p>
@ -125,8 +125,8 @@
</div>
]]></field>
</record>
</data>
<data>
<record id="email_template_loan_confirm_paid" model="mail.template">
<field name="name">Loan Issue Confirm Payment Received - Send by Email</field>
<field name="email_from">
@ -140,6 +140,8 @@
<field name="model_id"
ref="easy_my_coop_loan.model_loan_issue_line"/>
<field name="auto_delete" eval="True"/>
<field name="report_template" ref="easy_my_coop_loan.action_loan_issue_line_report"/>
<field name="report_name">Reimbursement table report</field>
<field name="lang">${object.partner_id.lang}</field>
<field name="easy_my_coop" eval="True"/>
<field name="body_html"><![CDATA[

33
easy_my_coop_loan/models/interest_line.py

@ -9,12 +9,23 @@ class LoanInterestLine(models.Model):
_name = "loan.interest.line"
_description = "Loan Interest Line"
name = fields.Integer(string="Year", required=True)
name = fields.Integer(
string="Year",
required=True
)
loan_issue_id = fields.Many2one(
related="issue_line.loan_issue_id",
store=True,
readlonly=True)
issue_line = fields.Many2one(
"loan.issue.line", string="Subscribed loan", required=True
"loan.issue.line",
string="Subscribed loan",
required=True
)
partner_id = fields.Many2one(
related="issue_line.partner_id", store=True, readlonly=True
related="issue_line.partner_id",
store=True,
readlonly=True
)
amount = fields.Monetary(
related="issue_line.amount",
@ -32,7 +43,10 @@ class LoanInterestLine(models.Model):
currency_field="company_currency_id",
readonly=True,
)
taxes_rate = fields.Float(string="Taxes on interest", required=True)
taxes_rate = fields.Float(
string="Taxes on interest",
required=True
)
taxes_amount = fields.Monetary(
string="Taxes amount",
currency_field="company_currency_id",
@ -58,6 +72,14 @@ class LoanInterestLine(models.Model):
currency_field="company_currency_id",
readonly=True,
)
due_loan_amount = fields.Monetary(
string="Due loan amount",
currency_field="company_currency_id"
)
due_amount = fields.Monetary(
string="Total due amount",
currency_field="company_currency_id"
)
due_date = fields.Date(string="Due date")
company_currency_id = fields.Many2one(
"res.currency",
@ -74,8 +96,9 @@ class LoanInterestLine(models.Model):
state = fields.Selection(
[
("draft", "Draft"),
("due_fy", "Due in the year"),
("due", "Due"),
("requested", "Payment requested"),
("scheduled", "Payment scheduled"),
("donation", "Donation"),
("paid", "Paid"),
],

73
easy_my_coop_loan/models/loan.py

@ -66,8 +66,19 @@ class LoanIssue(models.Model):
compute="_compute_subscribed_amount",
currency_field="company_currency_id",
)
capital_payment = fields.Selection(
[
("end", "End"),
("yearly", "Yearly")
],
string="Capital reimbursement"
)
interest_payment = fields.Selection(
[("end", "End"), ("yearly", "Yearly")], string="Interest payment"
[
("end", "End"),
("yearly", "Yearly")
],
string="Interest payment"
)
interest_payment_info = fields.Char(string="Yearly payment on")
loan_issue_lines = fields.One2many(
@ -172,57 +183,19 @@ class LoanIssue(models.Model):
self.ensure_one()
self.write({"state": "closed"})
def get_interest_vals(self, line, vals):
interest_obj = self.env["loan.interest.line"]
accrued_amount = line.amount
accrued_interest = 0
accrued_net_interest = 0
accrued_taxes = 0
for year in range(1, int(self.loan_term) + 1):
interest = accrued_amount * (line.loan_issue_id.rate / 100)
accrued_amount += interest
taxes_amount = interest * (self.taxes_rate / 100)
net_interest = interest - taxes_amount
accrued_interest += interest
accrued_net_interest += net_interest
accrued_taxes += taxes_amount
vals["interest"] = interest
vals["net_interest"] = net_interest
vals["taxes_amount"] = taxes_amount
vals["accrued_amount"] = accrued_amount
vals["accrued_interest"] = accrued_interest
vals["accrued_net_interest"] = accrued_net_interest
vals["accrued_taxes"] = accrued_taxes
vals["name"] = year
interest_obj.create(vals)
@api.multi
def compute_loan_interest(self):
self.ensure_one()
if self.interest_payment == "end":
due_date = self.term_date
else:
loan_term_year = self.loan_term / 12
if not (loan_term_year).is_integer():
# TODO Handle this case
raise NotImplementedError(
_("Interest payment by year hasn't been " "implemented yet")
)
for line in self.loan_issue_lines:
# TODO remove this line
line.interest_lines.unlink()
# Please Do not Forget
vals = {
"issue_line": line.id,
"due_date": due_date,
"taxes_rate": self.taxes_rate,
}
self.get_interest_vals(line, vals)
rounded_term = int(self.loan_term)
if self.loan_term - rounded_term > 0:
# TODO Handle this case
raise NotImplementedError(
_(
"Calculation on non entire year "
"hasn't been implemented yet"
)
_(
"Calculation on non entire year "
"hasn't been implemented yet"
)
)
lines = self.loan_issue_lines.filtered(lambda record:
record.state == "paid")
lines.action_compute_interest()

148
easy_my_coop_loan/models/loan_issue_line.py

@ -2,7 +2,9 @@
# Houssine BAKKALI <houssine@coopiteasy.be>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from datetime import datetime
import calendar
from datetime import date
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models
@ -18,14 +20,23 @@ class LoanIssueLine(models.Model):
for line in self:
line.amount = line.face_value * line.quantity
name = fields.Char(string="Reference")
name = fields.Char(
String="Name",
readonly=True,
)
reference = fields.Char(
string="Reference",
copy=False,
readonly=True,
states={'draft': [('readonly', False)]}
)
loan_issue_id = fields.Many2one(
"loan.issue", string="Loan issue", required=True
)
interest_lines = fields.One2many(
"loan.interest.line", "issue_line", string="Interest lines"
)
quantity = fields.Integer(string="quantity", required=True)
quantity = fields.Integer(string="Quantity", required=True)
face_value = fields.Monetary(
related="loan_issue_id.face_value",
currency_field="company_currency_id",
@ -33,13 +44,17 @@ class LoanIssueLine(models.Model):
readonly=True,
)
partner_id = fields.Many2one(
"res.partner", string="Subscriber", required=True
"res.partner",
string="Subscriber",
required=True
)
date = fields.Date(
string="Subscription date",
default=lambda self: datetime.strftime(datetime.now(), "%Y-%m-%d"),
default=lambda self: date.strftime(date.today(), "%Y-%m-%d"),
required=True,
)
payment_date = fields.Date(
string="Payment date")
amount = fields.Monetary(
string="Subscribed amount",
currency_field="company_currency_id",
@ -98,8 +113,13 @@ class LoanIssueLine(models.Model):
@api.multi
def action_validate(self):
sequence_id = self.env.ref(
"easy_my_coop_loan.sequence_loan_issue_line",
False
)
for line in self:
line.write({"state": "subscribed"})
loan_line_num = sequence_id.next_by_id()
line.write({"name": loan_line_num, "state": "subscribed"})
@api.multi
def action_request_payment(self):
@ -123,8 +143,120 @@ class LoanIssueLine(models.Model):
@api.multi
def action_paid(self):
loan_email_template = self.get_confirm_paid_email_template()
for line in self:
loan_email_template = self.get_confirm_paid_email_template()
vals = {"state": "paid"}
if not line.payment_date:
vals["payment_date"] = fields.Date.today()
line.write(vals)
line.action_compute_interest()
loan_email_template.sudo().send_mail(line.id, force_send=False)
line.write({"state": "paid"})
def get_number_of_days(self, year):
if calendar.isleap(year):
return 366
else:
return 365
@api.multi
def action_compute_interest(self):
for line in self:
loan_issue = line.loan_issue_id
vals = {
"issue_line": line.id,
"taxes_rate": loan_issue.taxes_rate,
}
list_vals = []
accrued_amount = self.amount
accrued_interest = 0
accrued_net_interest = 0
accrued_taxes = 0
taxes_amount = 0
diff_days = 0
# In case of a recompute is done. Only the interest lines
# in the future will be deleted. We also needed to determine
# from which year we'll have to regenerate the lines.
# Through the beautiful mind of Houssine, we implemented
# a small piece code to allows it.
today = fields.Date.today()
posted_lines = line.interest_lines.filtered(
lambda r: r.state == "paid" or
r.due_date < today)
futur_lines = line.interest_lines - posted_lines
start_to_line = len(posted_lines) + 1
futur_lines.unlink()
loan_term = int(loan_issue.loan_term / 12)
# if payment_date is first day of the month
# we take the current month
if line.payment_date.day == 1:
start_date = line.payment_date
else:
start_date = line.payment_date + relativedelta(months=+1,
day=1)
# we calculate the number of day between payment date
# and the end of the month of the payment
diff_days = ((start_date - relativedelta(days=1)) -
line.payment_date).days
rate = loan_issue.rate / 100
# take leap year into account
days = self.get_number_of_days(line.payment_date.year)
interim_amount = line.amount * rate * (diff_days / days)
due_date = start_date + relativedelta(years=loan_term)
for year in range(1, loan_term + 1):
interest = accrued_amount * rate
due_amount = 0
due_loan_amount = 0
if loan_issue.capital_payment == "end":
if year == loan_term:
due_amount = line.amount
else:
due_amount = line.amount * (loan_term / 100)
accrued_amount -= due_amount
due_loan_amount = due_amount
if year == 1:
interest += interim_amount
if loan_issue.interest_payment == "end":
accrued_interest += interest
accrued_amount += interest
net_interest = 0
if year == loan_term:
taxes_amount = (accrued_interest *
(loan_issue.taxes_rate / 100)
)
net_interest = accrued_interest - taxes_amount
due_amount += net_interest
else:
due_date = start_date + relativedelta(years=+year)
taxes_amount = interest * (loan_issue.taxes_rate / 100)
net_interest = interest - taxes_amount
due_amount += net_interest
accrued_interest = interest
accrued_net_interest += net_interest
accrued_taxes += taxes_amount
vals["due_date"] = due_date
vals["due_loan_amount"] = due_loan_amount
vals["due_amount"] = due_amount
vals["interest"] = interest
vals["net_interest"] = net_interest
vals["taxes_amount"] = taxes_amount
vals["accrued_amount"] = accrued_amount
vals["accrued_interest"] = accrued_interest
vals["accrued_net_interest"] = accrued_net_interest
vals["accrued_taxes"] = accrued_taxes
vals["name"] = year
if year >= start_to_line:
list_vals.append(vals.copy())
self.env["loan.interest.line"].create(list_vals)

111
easy_my_coop_loan/report/loan_issue_line_report.xml

@ -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>

13
easy_my_coop_loan/report/loan_report.xml

@ -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>

2
easy_my_coop_loan/security/ir.model.access.csv

@ -4,4 +4,4 @@ access_loan_issue_manager,loan.issue,model_loan_issue,easy_my_coop.group_easy_my
access_loan_issue_line_user,loan.issue.line,model_loan_issue_line,base.group_user,1,0,0,0
access_loan_issue_line_manager,loan.issue.line,model_loan_issue_line,easy_my_coop.group_easy_my_coop_user,1,1,1,0
access_loan_interest_line_user,loan.interest.line,model_loan_interest_line,base.group_user,1,0,0,0
access_loan_interest_line_manager,loan.interest.line,model_loan_interest_line,easy_my_coop.group_easy_my_coop_manager,1,1,1,0
access_loan_interest_line_manager,loan.interest.line,model_loan_interest_line,easy_my_coop.group_easy_my_coop_manager,1,1,1,1

96
easy_my_coop_loan/views/loan_interest_lines_view.xml

@ -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>

114
easy_my_coop_loan/views/loan_line_view.xml

@ -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>

111
easy_my_coop_loan/views/loan_view.xml

@ -102,6 +102,7 @@
<field name="loan_start_date"/>
<field name="term_date"/>
<field name="loan_term"/>
<field name="capital_payment" widget="selection"/>
<field name="interest_payment" widget="selection"/>
<field name="interest_payment_info"
attrs="{'invisible':[('interest_payment','not in',['end','yearly'])]}"/>
@ -109,7 +110,7 @@
</group>
<notebook>
<page name="lines" string="Lines">
<field name="loan_issue_lines">
<field name="loan_issue_lines" context="{'default_loan_issue_id': id}" attrs="{'readonly':[('state', '!=', 'ongoing')]}">
<tree delete="false">
<field name="name"/>
<field name="partner_id"/>
@ -145,110 +146,4 @@
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<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="loan_issue_id"/>
<field name="partner_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 issue">
<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_cancel" string="Cancel" type="object"
states="draft,subscribed,waiting"
confirm="Are you sure you want to cancel this loan subscription ?"/>
<button name="action_draft" string="Set to draft"
type="object" states="cancelled"/>
<button name="action_paid" string="Paid"
type="object" states="waiting"/>
<field name="state" widget="statusbar"/>
</header>
<sheet>
<group>
<group>
<field name="loan_issue_id"/>
<field name="name"/>
<field name="quantity"/>
<field name="face_value"/>
</group>
<group>
<field name="date"/>
<field name="partner_id"/>
<field name="amount"/>
</group>
</group>
<notebook>
<page 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="net_interest"/>
<field name="taxes_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>
</odoo>

2
easy_my_coop_loan_account/__init__.py

@ -0,0 +1,2 @@
from . import models
from . import wizard

29
easy_my_coop_loan_account/__manifest__.py

@ -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,
}

31
easy_my_coop_loan_account/data/emc_loan_account_data.xml

@ -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>

5
easy_my_coop_loan_account/models/__init__.py

@ -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

46
easy_my_coop_loan_account/models/account_fiscal_year.py

@ -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

39
easy_my_coop_loan_account/models/account_move.py

@ -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)

92
easy_my_coop_loan_account/models/company.py

@ -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,
)

200
easy_my_coop_loan_account/models/interest_line.py

@ -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

71
easy_my_coop_loan_account/models/loan_issue_line.py

@ -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"))

22
easy_my_coop_loan_account/views/loan_issue_line_view.xml

@ -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>

25
easy_my_coop_loan_account/views/res_company_view.xml

@ -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>

1
easy_my_coop_loan_account/wizard/__init__.py

@ -0,0 +1 @@
from . import end_of_year_operation

77
easy_my_coop_loan_account/wizard/end_of_year_operation.py

@ -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)
])

35
easy_my_coop_loan_account/wizard/end_of_year_operation.xml

@ -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>

1
easy_my_coop_loan_bba/__init__.py

@ -0,0 +1 @@
from . import models

21
easy_my_coop_loan_bba/__manifest__.py

@ -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,
}

1
easy_my_coop_loan_bba/models/__init__.py

@ -0,0 +1 @@
from . import loan_issue_line

95
easy_my_coop_loan_bba/models/loan_issue_line.py

@ -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})

11
easy_my_coop_loan_website/__manifest__.py

@ -5,7 +5,11 @@
{
"name": "Easy My Coop Loan Issues Website",
"version": "12.0.1.0.1",
"depends": ["easy_my_coop_loan", "easy_my_coop_website", "website"],
"depends": [
"easy_my_coop_loan",
"easy_my_coop_website",
"website",
],
"author": "Coop IT Easy SCRLfs",
"category": "Cooperative management",
"website": "http://www.coopiteasy.be",
@ -14,6 +18,9 @@
This module implements the subscription page
for bonds and subordinated loans.
""",
"data": ["data/website_loan_data.xml", "template/loan_issue_template.xml"],
"data": [
"data/website_loan_data.xml",
"template/loan_issue_template.xml"
],
"installable": True,
}

26
easy_my_coop_loan_website/controllers/main.py

@ -26,6 +26,27 @@ class WebsiteLoanIssueSubscription(http.Controller):
else:
return False
def missing_mandatory_info(self):
partner = request.env.user.partner_id
if (
not partner.bank_ids
or not partner.birthdate_date
or not partner.street
or not partner.city
or not partner.zip
or not partner.country_id
or not partner.gender
or not partner.phone
):
return False
if partner.is_company:
if (
not partner.company_name
or not partner.vat
):
return False
return True
@http.route(
["/subscription/loan_issue_form"],
type="http",
@ -34,8 +55,9 @@ class WebsiteLoanIssueSubscription(http.Controller):
)
def display_loan_issue_subscription_page(self, **kwargs):
values = {}
partner = request.env.user.partner_id
is_company = partner.is_company
if not self.missing_mandatory_info():
return request.redirect("/my/account")
is_company = request.env.user.partner_id.is_company
values = self.fill_values(values, is_company)
values.update(kwargs=kwargs.items())

6
easy_my_coop_website/__manifest__.py

@ -6,7 +6,11 @@
{
"name": "Easy My Coop Website",
"version": "12.0.1.0.4",
"depends": ["easy_my_coop", "website", "website_recaptcha_reloaded"],
"depends": [
"easy_my_coop",
"website",
"website_recaptcha_reloaded",
],
"author": "Coop IT Easy SCRLfs",
"category": "Cooperative management",
"website": "https://coopiteasy.be",

9
easy_my_coop_website_portal/__manifest__.py

@ -4,8 +4,13 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Easy My Coop Website Portal",
"version": "12.0.1.0.0",
"depends": ["easy_my_coop", "website", "account", "portal"],
"version": "12.0.1.0.1",
"depends": [
"easy_my_coop",
"website",
"account",
"portal"
],
"summary": """
Show cooperator information in the website portal.
""",

6
easy_my_coop_website_portal/controllers/main.py

@ -18,7 +18,7 @@ from odoo.addons.portal.controllers.portal import (
class CooperatorPortalAccount(CustomerPortal):
CustomerPortal.MANDATORY_BILLING_FIELDS.extend(
["iban", "birthdate_date", "gender"]
["iban", "birthdate_date", "gender", "lang"]
)
def _prepare_portal_layout_values(self):
@ -62,6 +62,7 @@ class CooperatorPortalAccount(CustomerPortal):
"invoice_count": invoice_count,
"iban": iban,
"genders": fields_desc["gender"]["selection"],
"langs": request.env["res.lang"].search([])
}
)
return values
@ -81,9 +82,10 @@ class CooperatorPortalAccount(CustomerPortal):
@route(["/my/account"], type="http", auth="user", website=True)
def account(self, redirect=None, **post):
partner = request.env.user.partner_id
res = super(CooperatorPortalAccount, self).account(redirect, **post)
if not res.qcontext.get("error"):
partner = request.env.user.partner_id
partner_bank = request.env["res.partner.bank"]
iban = post.get("iban")
if iban:

14
easy_my_coop_website_portal/views/easy_my_coop_website_portal_templates.xml

@ -28,6 +28,20 @@
</t>
</select>
</div>
<div t-attf-class="form-group #{error.get('lang') and 'has-error' or ''} col-xl-6">
<label class="col-form-label" for="lang">Language</label>
<select name="lang"
t-attf-class="form-control #{error.get('lang') or ''}">
<option value=""></option>
<t t-foreach="langs or []"
t-as="langue">
<option t-att-value="langue.code"
t-att-selected="langue.code == partner.lang">
<t t-esc="langue.name"/>
</option>
</t>
</select>
</div>
<div t-attf-class="form-group #{error.get('birthdate_date') and 'o_has_error' or ''} col-xl-6">
<label class="col-form-label" for="birthdate_date">Birthdate
</label>

Loading…
Cancel
Save