Skip to content

Commit

Permalink
account_cutoff_start_end_dates: forecast is now a specific state
Browse files Browse the repository at this point in the history
To simplify the code and the user interface, the forecast mode doesn't
use a boolean field any more; it is now a specific "state".
Migration script is provided.
  • Loading branch information
alexis-via authored and dzungtran89 committed Feb 16, 2023
1 parent 363ab49 commit f71ee61
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 82 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2023 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).


def migrate(cr, version):
if not version:
return

cr.execute(
"UPDATE account_cutoff SET state='forecast' "
"WHERE state='draft' AND forecast is true"
)
37 changes: 20 additions & 17 deletions account_cutoff_start_end_dates/models/account_cutoff.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,15 @@ def _get_default_source_journals(self):
check_company=True,
domain="[('company_id', '=', company_id)]",
)
forecast = fields.Boolean(
readonly=True,
tracking=True,
help="The Forecast mode allows the user to compute "
"the prepaid revenue/expense between 2 dates in the future.",
)
start_date = fields.Date()
end_date = fields.Date()
state = fields.Selection(selection_add=[("forecast", "Forecast")])
start_date = fields.Date(help="This field is only for the forecast mode")
end_date = fields.Date(help="This field is only for the forecast mode")

@api.constrains("start_date", "end_date", "forecast")
@api.constrains("start_date", "end_date", "state")
def _check_start_end_dates(self):
for rec in self:
if (
rec.forecast
rec.state == "forecast"
and rec.start_date
and rec.end_date
and rec.start_date > rec.end_date
Expand All @@ -71,13 +66,14 @@ def forecast_enable(self):
)
)
self.line_ids.unlink()
self.write({"forecast": True})
# set cutoff_date to False to avoid issue with unicity sql constraint
self.write({"state": "forecast", "cutoff_date": False})

def forecast_disable(self):
self.ensure_one()
assert self.state == "draft"
assert self.state == "forecast"
self.line_ids.unlink()
self.write({"forecast": False})
self.write({"state": "draft"})

def _prepare_date_cutoff_line(self, aml, mapping):
self.ensure_one()
Expand Down Expand Up @@ -147,7 +143,7 @@ def _prepare_date_prepaid_cutoff_line(self, aml, vals):
end_date_dt = aml.end_date
# Here, we compute the amount of the cutoff
# That's the important part !
if self.forecast:
if self.state == "forecast":
out_days = 0
forecast_start_date_dt = self.start_date
forecast_end_date_dt = self.end_date
Expand All @@ -173,7 +169,7 @@ def _prepare_date_prepaid_cutoff_line(self, aml, vals):
)

def get_lines(self):
res = super().get_lines()
super().get_lines()
aml_obj = self.env["account.move.line"]
line_obj = self.env["account.cutoff.line"]
if not self.source_journal_ids:
Expand All @@ -185,9 +181,17 @@ def get_lines(self):
("company_id", "=", self.company_id.id),
("balance", "!=", 0),
]
if self.source_move_state == "posted":
domain.append(("parent_state", "=", "posted"))
else:
domain.append(("parent_state", "in", ("draft", "posted")))

if self.cutoff_type in ["prepaid_expense", "prepaid_revenue"]:
if self.forecast:
if self.state == "forecast":
if not self.start_date or not self.end_date:
raise UserError(
_("Start date and end date are required for forecast mode.")
)
domain += [
("start_date", "!=", False),
("start_date", "<=", self.end_date),
Expand All @@ -208,4 +212,3 @@ def get_lines(self):
amls = aml_obj.search(domain)
for aml in amls:
line_obj.create(self._prepare_date_cutoff_line(aml, mapping))
return res
91 changes: 26 additions & 65 deletions account_cutoff_start_end_dates/views/account_cutoff.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,26 @@
<field name="inherit_id" ref="account_cutoff_base.account_cutoff_tree" />
<field name="arch" type="xml">
<field name="cutoff_date" position="after">
<field
name="forecast"
invisible="'prepaid' not in context.get('default_cutoff_type', '-')"
optional="show"
/>
<field
name="start_date"
invisible="'prepaid' not in context.get('default_cutoff_type', '-')"
optional="show"
attrs="{'invisible': [('forecast', '=', False)]}"
optional="hide"
attrs="{'invisible': [('state', '!=', 'forecast')]}"
/>
<field
name="end_date"
invisible="'prepaid' not in context.get('default_cutoff_type', '-')"
optional="show"
attrs="{'invisible': [('forecast', '=', False)]}"
optional="hide"
attrs="{'invisible': [('state', '!=', 'forecast')]}"
/>
</field>
<field name="cutoff_date" position="attributes">
<attribute
name="attrs"
>{'invisible': [('forecast', '=', True)]}</attribute>
>{'invisible': [('state', '=', 'forecast')]}</attribute>
</field>
<field name="state" position="attributes">
<attribute name="decoration-warning">state == 'forecast'</attribute>
</field>
</field>
</record>
Expand All @@ -46,86 +44,49 @@
name="forecast_enable"
type="object"
string="Enable Forecast Mode"
attrs="{'invisible': ['|', '|', ('state', '!=', 'draft'), ('forecast', '=', True), ('cutoff_type', 'not in', ('prepaid_revenue', 'prepaid_expense'))]}"
attrs="{'invisible': ['|', ('state', '!=', 'draft'), ('cutoff_type', 'not in', ('prepaid_revenue', 'prepaid_expense'))]}"
/>
<button
name="forecast_disable"
type="object"
string="Leave Forecast Mode"
attrs="{'invisible': ['|', '|', ('state', '!=', 'draft'), ('forecast', '=', False), ('cutoff_type', 'not in', ('prepaid_revenue', 'prepaid_expense'))]}"
states="forecast"
/>
</button>
<field name="cutoff_date" position="before">
<field
name="forecast"
attrs="{'invisible': [('forecast', '=', False)]}"
/>
<field
name="start_date"
attrs="{'invisible': [('forecast', '=', False)], 'required': [('forecast', '=', True)]}"
/>
<field
name="end_date"
attrs="{'invisible': [('forecast', '=', False)], 'required': [('forecast', '=', True)]}"
/>
<!-- I don't set the fields start_date and end_date
as required, to make it easier for users
to leave forecast mode if they entered it by mistake -->
<field name="start_date" states="forecast" />
<field name="end_date" states="forecast" />
</field>
<field name="cutoff_date" position="attributes">
<attribute
name="attrs"
>{'invisible': [('forecast', '=', True)], 'required': [('forecast', '=', False)]}</attribute>
<attribute name="required">0</attribute>
<attribute name="states">draft,done</attribute>
</field>
<field name="state" position="attributes">
<attribute name="statusbar_visible">draft,done</attribute>
</field>
<group name="accounting-params" position="attributes">
<attribute
name="attrs"
>{'invisible': [('forecast', '=', True)]}</attribute>
<attribute name="states">draft,done</attribute>
</group>
<field name="cutoff_journal_id" position="attributes">
<attribute name="required">0</attribute>
<attribute
name="attrs"
>{'required': [('forecast', '=', False)]}</attribute>
</field>
<field name="cutoff_account_id" position="attributes">
<attribute name="required">0</attribute>
<attribute
name="attrs"
>{'required': [('forecast', '=', False)]}</attribute>
</field>
<field name="move_label" position="attributes">
<attribute name="required">0</attribute>
<attribute
name="attrs"
>{'required': [('forecast', '=', False)]}</attribute>
</field>
<button name="create_move" position="attributes">
<attribute
name="attrs"
>{'invisible': ['|', '|', ('line_ids', '=', False), ('state', '=', 'done'), ('forecast', '=', True)]}</attribute>
</button>
<field name="cutoff_date" position="after">
<field name="source_move_state" position="after">
<field name="source_journal_ids" widget="many2many_tags" />
</field>
<button name="get_lines" position="attributes">
<attribute name="states">draft,forecast</attribute>
</button>
</field>
</record>
<record id="account_cutoff_filter" model="ir.ui.view">
<field name="name">account.cutoff.start.end.dates.search</field>
<field name="model">account.cutoff</field>
<field name="inherit_id" ref="account_cutoff_base.account_cutoff_filter" />
<field name="arch" type="xml">
<filter name="done" position="after">
<separator />
<filter name="draft" position="after">
<filter
name="forecast"
string="Forecast"
domain="[('forecast', '=', True)]"
/>
</filter>
<filter name="state_groupby" position="after">
<filter
name="forecast_groupby"
string="Forecast"
context="{'group_by': 'forecast'}"
domain="[('state', '=', 'forecast')]"
/>
</filter>
</field>
Expand Down

0 comments on commit f71ee61

Please sign in to comment.