Compare commits
3 Commits
om_account
...
om_account
| Author | SHA1 | Date | |
|---|---|---|---|
| ee9b1958f1 | |||
| 48db592326 | |||
| 8f834373b7 |
3
addons/om_account_asset/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from . import wizard
|
||||
from . import models
|
||||
from . import report
|
||||
32
addons/om_account_asset/__manifest__.py
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
'name': 'Odoo 19 Assets Management',
|
||||
'version': '19.0.1.0.0', # __odoosky_original_version__: '1.0.0'
|
||||
'author': 'Odoo Mates, Odoo SA',
|
||||
'depends': ['account'],
|
||||
'description': """Manage assets owned by a company or a person.
|
||||
Keeps track of depreciation's, and creates corresponding journal entries""",
|
||||
'summary': 'Odoo 19 Assets Management',
|
||||
'category': 'Accounting',
|
||||
'sequence': 10,
|
||||
'website': 'https://www.odoomates.tech',
|
||||
'license': 'LGPL-3',
|
||||
'images': ['static/description/assets.gif'],
|
||||
'data': [
|
||||
'data/account_asset_data.xml',
|
||||
'security/account_asset_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'wizard/asset_depreciation_confirmation_wizard_views.xml',
|
||||
'wizard/asset_modify_views.xml',
|
||||
'views/account_asset_views.xml',
|
||||
'views/account_move_views.xml',
|
||||
'views/account_asset_templates.xml',
|
||||
'views/asset_category_views.xml',
|
||||
'views/product_views.xml',
|
||||
'report/account_asset_report_views.xml',
|
||||
],
|
||||
'assets': {
|
||||
'web.assets_backend': [
|
||||
'om_account_asset/static/src/scss/account_asset.scss',
|
||||
],
|
||||
},
|
||||
}
|
||||
17
addons/om_account_asset/data/account_asset_data.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding='UTF-8'?>
|
||||
<odoo>
|
||||
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="account_asset_cron" model="ir.cron">
|
||||
<field name="name">Account Asset: Generate asset entries</field>
|
||||
<field name="model_id" ref="model_account_asset_asset"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model._cron_generate_entries()</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">months</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
||||
</odoo>
|
||||
1280
addons/om_account_asset/i18n/ar_001.po
Normal file
1273
addons/om_account_asset/i18n/ar_SY.po
Normal file
1236
addons/om_account_asset/i18n/es_AR.po
Normal file
1357
addons/om_account_asset/i18n/fr.po
Normal file
1319
addons/om_account_asset/i18n/tr.po
Normal file
1288
addons/om_account_asset/i18n/uk.po
Normal file
1435
addons/om_account_asset/i18n/zh_TW.po
Normal file
6
addons/om_account_asset/models/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account
|
||||
from . import account_asset
|
||||
from . import account_move
|
||||
from . import product
|
||||
23
addons/om_account_asset/models/account.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = 'account.move'
|
||||
|
||||
asset_depreciation_ids = fields.One2many('account.asset.depreciation.line', 'move_id',
|
||||
string='Assets Depreciation Lines')
|
||||
|
||||
def button_cancel(self):
|
||||
for move in self:
|
||||
for line in move.asset_depreciation_ids:
|
||||
line.move_posted_check = False
|
||||
return super(AccountMove, self).button_cancel()
|
||||
|
||||
def action_post(self):
|
||||
for move in self:
|
||||
for depreciation_line in move.asset_depreciation_ids:
|
||||
depreciation_line.post_lines_and_close_asset()
|
||||
return super(AccountMove, self).action_post()
|
||||
728
addons/om_account_asset/models/account_asset.py
Normal file
@@ -0,0 +1,728 @@
|
||||
import calendar
|
||||
from datetime import date, datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools import float_compare, float_is_zero
|
||||
from markupsafe import Markup
|
||||
|
||||
|
||||
class AccountAssetCategory(models.Model):
|
||||
_name = 'account.asset.category'
|
||||
_description = 'Asset category'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin', 'analytic.mixin']
|
||||
|
||||
exclude_types = ['asset_receivable', 'asset_cash', 'liability_payable',
|
||||
'liability_credit_card', 'equity', 'equity_unaffected']
|
||||
|
||||
active = fields.Boolean(default=True)
|
||||
name = fields.Char(required=True, index=True, string="Asset Type")
|
||||
account_analytic_id = fields.Many2one('account.analytic.account', string='Analytic Account')
|
||||
account_asset_id = fields.Many2one(
|
||||
'account.account', string='Asset Account',
|
||||
required=True,
|
||||
domain=[('account_type', 'not in', exclude_types)],
|
||||
help="Account used to record the purchase of the asset at its original price."
|
||||
)
|
||||
account_depreciation_id = fields.Many2one(
|
||||
'account.account', string='Depreciation Entries: Asset Account',
|
||||
required=True,
|
||||
domain=[('account_type', 'not in', exclude_types)],
|
||||
help="Account used in the depreciation entries, to decrease the asset value."
|
||||
)
|
||||
account_depreciation_expense_id = fields.Many2one(
|
||||
'account.account', string='Depreciation Entries: Expense Account',
|
||||
required=True,
|
||||
domain=[('account_type', 'not in', exclude_types)],
|
||||
help="Account used in the periodical entries "
|
||||
"to record a part of the asset as expense."
|
||||
)
|
||||
journal_id = fields.Many2one(
|
||||
'account.journal', string='Journal', required=True
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
'res.company', string='Company',
|
||||
required=True, default=lambda self: self.env.company
|
||||
)
|
||||
method = fields.Selection(
|
||||
[('linear', 'Linear'), ('degressive', 'Degressive')],
|
||||
string='Computation Method', required=True, default='linear',
|
||||
help="Choose the method to use to compute the amount of depreciation lines.\n"
|
||||
" * Linear: Calculated on basis of: Gross Value / Number of Depreciations\n"
|
||||
" * Degressive: Calculated on basis of: Residual Value * Degressive Factor"
|
||||
)
|
||||
method_number = fields.Integer(
|
||||
string='Number of Depreciations', default=5,
|
||||
help="The number of depreciations needed to depreciate your asset"
|
||||
)
|
||||
method_period = fields.Integer(
|
||||
string='Period Length', default=1,
|
||||
help="State here the time between 2 depreciations, in months", required=True
|
||||
)
|
||||
method_progress_factor = fields.Float(
|
||||
'Degressive Factor', default=0.3
|
||||
)
|
||||
method_time = fields.Selection(
|
||||
[('number', 'Number of Entries'), ('end', 'Ending Date')],
|
||||
string='Time Method', required=True, default='number',
|
||||
help="Choose the method to use to compute the dates and number of entries.\n"
|
||||
" * Number of Entries: Fix the number of entries and the time between 2 depreciations.\n"
|
||||
" * Ending Date: Choose the time between 2 depreciations and the date the depreciations won't go beyond."
|
||||
)
|
||||
method_end = fields.Date('Ending date')
|
||||
prorata = fields.Boolean(
|
||||
string='Prorata Temporis',
|
||||
help='Indicates that the first depreciation entry for this asset have to be done from the '
|
||||
'purchase date instead of the first of January'
|
||||
)
|
||||
open_asset = fields.Boolean(
|
||||
string='Auto-Confirm Assets',
|
||||
help="Check this if you want to automatically confirm the assets "
|
||||
"of this category when created by invoices."
|
||||
)
|
||||
group_entries = fields.Boolean(
|
||||
string='Group Journal Entries',
|
||||
help="Check this if you want to group the generated entries by categories."
|
||||
)
|
||||
type = fields.Selection(
|
||||
[('sale', 'Sale: Revenue Recognition'), ('purchase', 'Purchase: Asset')],
|
||||
required=True, index=True, default='purchase'
|
||||
)
|
||||
date_first_depreciation = fields.Selection([
|
||||
('last_day_period', 'Based on Last Day of Purchase Period'),
|
||||
('manual', 'Manual (Defaulted on Purchase Date)')],
|
||||
string='Depreciation Dates', default='manual', required=True,
|
||||
help='The way to compute the date of the first depreciation.\n'
|
||||
' * Based on last day of purchase period: The depreciation dates will'
|
||||
' be based on the last day of the purchase month or the purchase'
|
||||
' year (depending on the periodicity of the depreciations).\n'
|
||||
' * Based on purchase date: The depreciation dates will be based on the purchase date.')
|
||||
|
||||
@api.onchange('account_asset_id')
|
||||
def onchange_account_asset(self):
|
||||
if self.type == "purchase":
|
||||
self.account_depreciation_id = self.account_asset_id
|
||||
elif self.type == "sale":
|
||||
self.account_depreciation_expense_id = self.account_asset_id
|
||||
|
||||
@api.onchange('type')
|
||||
def onchange_type(self):
|
||||
if self.type == 'sale':
|
||||
self.prorata = True
|
||||
self.method_period = 1
|
||||
else:
|
||||
self.method_period = 12
|
||||
|
||||
@api.onchange('method_time')
|
||||
def _onchange_method_time(self):
|
||||
if self.method_time != 'number':
|
||||
self.prorata = False
|
||||
|
||||
|
||||
class AccountAssetAsset(models.Model):
|
||||
_name = 'account.asset.asset'
|
||||
_description = 'Asset/Revenue Recognition'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin', 'analytic.mixin']
|
||||
|
||||
entry_count = fields.Integer(compute='_entry_count', string='# Asset Entries')
|
||||
name = fields.Char(string='Asset Name', required=True)
|
||||
code = fields.Char(string='Reference', size=32)
|
||||
value = fields.Monetary(string='Gross Value', required=True)
|
||||
currency_id = fields.Many2one(
|
||||
'res.currency', string='Currency', required=True,
|
||||
default=lambda self: self.env.user.company_id.currency_id.id
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
'res.company', string='Company', required=True,
|
||||
default=lambda self: self.env.company)
|
||||
note = fields.Text()
|
||||
category_id = fields.Many2one(
|
||||
'account.asset.category', string='Category',
|
||||
required=True, change_default=True
|
||||
)
|
||||
date = fields.Date(string='Date', required=True, default=fields.Date.context_today)
|
||||
state = fields.Selection([('draft', 'Draft'), ('open', 'Running'), ('close', 'Close')],
|
||||
'Status', required=True, copy=False, default='draft',
|
||||
help="When an asset is created, the status is 'Draft'.\n"
|
||||
"If the asset is confirmed, the status goes in 'Running' and the depreciation "
|
||||
"lines can be posted in the accounting.\n"
|
||||
"You can manually close an asset when the depreciation is over. If the last line"
|
||||
" of depreciation is posted, the asset automatically goes in that status.")
|
||||
active = fields.Boolean(default=True)
|
||||
partner_id = fields.Many2one('res.partner', string='Partner')
|
||||
method = fields.Selection(
|
||||
[('linear', 'Linear'), ('degressive', 'Degressive')],
|
||||
string='Computation Method', required=True, default='linear',
|
||||
help="Choose the method to use to compute the amount of depreciation lines.\n * Linear:"
|
||||
" Calculated on basis of: Gross Value / Number of Depreciations\n"
|
||||
" * Degressive: Calculated on basis of: Residual Value * Degressive Factor"
|
||||
)
|
||||
method_number = fields.Integer(string='Number of Depreciations', default=5,
|
||||
help="The number of depreciations needed to depreciate your asset")
|
||||
method_period = fields.Integer(
|
||||
string='Number of Months in a Period', required=True, default=12,
|
||||
help="The amount of time between two depreciations, in months"
|
||||
)
|
||||
method_end = fields.Date(string='Ending Date')
|
||||
method_progress_factor = fields.Float(
|
||||
string='Degressive Factor', default=0.3
|
||||
)
|
||||
value_residual = fields.Monetary(compute='_amount_residual', string='Residual Value')
|
||||
method_time = fields.Selection(
|
||||
[('number', 'Number of Entries'), ('end', 'Ending Date')],
|
||||
string='Time Method', required=True, default='number',
|
||||
help="Choose the method to use to compute the dates and number of entries.\n"
|
||||
" * Number of Entries: Fix the number of entries and the time between 2 depreciations.\n"
|
||||
" * Ending Date: Choose the time between 2 depreciations and the date the depreciations won't go beyond."
|
||||
)
|
||||
prorata = fields.Boolean(
|
||||
string='Prorata Temporis',
|
||||
help='Indicates that the first depreciation entry for this asset'
|
||||
' have to be done from the asset date (purchase date) '
|
||||
'instead of the first January / Start date of fiscal year'
|
||||
)
|
||||
depreciation_line_ids = fields.One2many(
|
||||
'account.asset.depreciation.line', 'asset_id', string='Depreciation Lines'
|
||||
)
|
||||
salvage_value = fields.Monetary(
|
||||
string='Salvage Value',
|
||||
help="It is the amount you plan to have that you cannot depreciate."
|
||||
)
|
||||
invoice_id = fields.Many2one('account.move', string='Invoice', copy=False)
|
||||
type = fields.Selection(related="category_id.type", string='Type', required=True)
|
||||
account_analytic_id = fields.Many2one('account.analytic.account', string='Analytic Account')
|
||||
date_first_depreciation = fields.Selection([
|
||||
('last_day_period', 'Based on Last Day of Purchase Period'),
|
||||
('manual', 'Manual')],
|
||||
string='Depreciation Dates', default='manual',
|
||||
required=True,
|
||||
help='The way to compute the date of the first depreciation.\n'
|
||||
' * Based on last day of purchase period: The depreciation'
|
||||
' dates will be based on the last day of the purchase month or the '
|
||||
'purchase year (depending on the periodicity of the depreciations).\n'
|
||||
' * Based on purchase date: The depreciation dates will be based on the purchase date.\n')
|
||||
first_depreciation_manual_date = fields.Date(
|
||||
string='First Depreciation Date',
|
||||
help='Note that this date does not alter the computation of the first '
|
||||
'journal entry in case of prorata temporis assets. It simply changes its accounting date'
|
||||
)
|
||||
|
||||
def unlink(self):
|
||||
for asset in self:
|
||||
if asset.state in ['open', 'close']:
|
||||
raise UserError(_('You cannot delete a document that is in %s state.') % (asset.state,))
|
||||
for depreciation_line in asset.depreciation_line_ids:
|
||||
if depreciation_line.move_id:
|
||||
raise UserError(_('You cannot delete a document that contains posted entries.'))
|
||||
return super(AccountAssetAsset, self).unlink()
|
||||
|
||||
@api.model
|
||||
def _cron_generate_entries(self):
|
||||
self.compute_generated_entries(datetime.today())
|
||||
|
||||
@api.model
|
||||
def compute_generated_entries(self, date, asset_type=None):
|
||||
# Entries generated : one by grouped category and one by asset from ungrouped category
|
||||
created_move_ids = []
|
||||
type_domain = []
|
||||
if asset_type:
|
||||
type_domain = [('type', '=', asset_type)]
|
||||
|
||||
ungrouped_assets = self.env['account.asset.asset'].search(type_domain + [('state', '=', 'open'), ('category_id.group_entries', '=', False)])
|
||||
created_move_ids += ungrouped_assets._compute_entries(date, group_entries=False)
|
||||
|
||||
for grouped_category in self.env['account.asset.category'].search(type_domain + [('group_entries', '=', True)]):
|
||||
assets = self.env['account.asset.asset'].search([('state', '=', 'open'), ('category_id', '=', grouped_category.id)])
|
||||
created_move_ids += assets._compute_entries(date, group_entries=True)
|
||||
return created_move_ids
|
||||
|
||||
def _compute_board_amount(self, sequence, residual_amount, amount_to_depr,
|
||||
undone_dotation_number, posted_depreciation_line_ids,
|
||||
total_days, depreciation_date):
|
||||
amount = 0
|
||||
if sequence == undone_dotation_number:
|
||||
amount = residual_amount
|
||||
else:
|
||||
if self.method == 'linear':
|
||||
amount = amount_to_depr / (undone_dotation_number - len(posted_depreciation_line_ids))
|
||||
if self.prorata:
|
||||
amount = amount_to_depr / self.method_number
|
||||
if sequence == 1:
|
||||
date = self.date
|
||||
if self.method_period % 12 != 0:
|
||||
month_days = calendar.monthrange(date.year, date.month)[1]
|
||||
days = month_days - date.day + 1
|
||||
amount = (amount_to_depr / self.method_number) / month_days * days
|
||||
else:
|
||||
days = (self.company_id.compute_fiscalyear_dates(date)['date_to'] - date).days + 1
|
||||
amount = (amount_to_depr / self.method_number) / total_days * days
|
||||
elif self.method == 'degressive':
|
||||
amount = residual_amount * self.method_progress_factor
|
||||
if self.prorata:
|
||||
if sequence == 1:
|
||||
date = self.date
|
||||
if self.method_period % 12 != 0:
|
||||
month_days = calendar.monthrange(date.year, date.month)[1]
|
||||
days = month_days - date.day + 1
|
||||
amount = (residual_amount * self.method_progress_factor) / month_days * days
|
||||
else:
|
||||
days = (self.company_id.compute_fiscalyear_dates(date)['date_to'] - date).days + 1
|
||||
amount = (residual_amount * self.method_progress_factor) / total_days * days
|
||||
return amount
|
||||
|
||||
def _compute_board_undone_dotation_nb(self, depreciation_date, total_days):
|
||||
undone_dotation_number = self.method_number
|
||||
if self.method_time == 'end':
|
||||
end_date = self.method_end
|
||||
undone_dotation_number = 0
|
||||
while depreciation_date <= end_date:
|
||||
depreciation_date = date(depreciation_date.year, depreciation_date.month,
|
||||
depreciation_date.day) + relativedelta(months=+self.method_period)
|
||||
undone_dotation_number += 1
|
||||
if self.prorata:
|
||||
undone_dotation_number += 1
|
||||
return undone_dotation_number
|
||||
|
||||
def compute_depreciation_board(self):
|
||||
self.ensure_one()
|
||||
|
||||
posted_depreciation_line_ids = self.depreciation_line_ids.filtered(lambda x: x.move_check).sorted(key=lambda l: l.depreciation_date)
|
||||
unposted_depreciation_line_ids = self.depreciation_line_ids.filtered(lambda x: not x.move_check)
|
||||
|
||||
# Remove old unposted depreciation lines. We cannot use unlink() with One2many field
|
||||
commands = [(2, line_id.id, False) for line_id in unposted_depreciation_line_ids]
|
||||
|
||||
if self.value_residual != 0.0:
|
||||
amount_to_depr = residual_amount = self.value_residual
|
||||
|
||||
# if we already have some previous validated entries, starting date is last entry + method period
|
||||
if posted_depreciation_line_ids and posted_depreciation_line_ids[-1].depreciation_date:
|
||||
last_depreciation_date = fields.Date.from_string(posted_depreciation_line_ids[-1].depreciation_date)
|
||||
depreciation_date = last_depreciation_date + relativedelta(months=+self.method_period)
|
||||
else:
|
||||
# depreciation_date computed from the purchase date
|
||||
depreciation_date = self.date
|
||||
if self.date_first_depreciation == 'last_day_period':
|
||||
# depreciation_date = the last day of the month
|
||||
depreciation_date = depreciation_date + relativedelta(day=31)
|
||||
# ... or fiscalyear depending the number of period
|
||||
if self.method_period == 12:
|
||||
depreciation_date = depreciation_date + relativedelta(month=int(self.company_id.fiscalyear_last_month))
|
||||
depreciation_date = depreciation_date + relativedelta(day=int(self.company_id.fiscalyear_last_day))
|
||||
if depreciation_date < self.date:
|
||||
depreciation_date = depreciation_date + relativedelta(years=1)
|
||||
elif self.first_depreciation_manual_date and self.first_depreciation_manual_date != self.date:
|
||||
# depreciation_date set manually from the 'first_depreciation_manual_date' field
|
||||
depreciation_date = self.first_depreciation_manual_date
|
||||
total_days = (depreciation_date.year % 4) and 365 or 366
|
||||
month_day = depreciation_date.day
|
||||
undone_dotation_number = self._compute_board_undone_dotation_nb(depreciation_date, total_days)
|
||||
|
||||
for x in range(len(posted_depreciation_line_ids), undone_dotation_number):
|
||||
sequence = x + 1
|
||||
amount = self._compute_board_amount(sequence, residual_amount, amount_to_depr,
|
||||
undone_dotation_number, posted_depreciation_line_ids,
|
||||
total_days, depreciation_date)
|
||||
amount = self.currency_id.round(amount)
|
||||
if float_is_zero(amount, precision_rounding=self.currency_id.rounding):
|
||||
continue
|
||||
residual_amount -= amount
|
||||
vals = {
|
||||
'amount': amount,
|
||||
'asset_id': self.id,
|
||||
'sequence': sequence,
|
||||
'name': (self.code or '') + '/' + str(sequence),
|
||||
'remaining_value': residual_amount,
|
||||
'depreciated_value': self.value - (self.salvage_value + residual_amount),
|
||||
'depreciation_date': depreciation_date,
|
||||
}
|
||||
commands.append((0, False, vals))
|
||||
|
||||
depreciation_date = depreciation_date + relativedelta(months=+self.method_period)
|
||||
|
||||
if month_day > 28 and self.date_first_depreciation == 'manual':
|
||||
max_day_in_month = calendar.monthrange(depreciation_date.year, depreciation_date.month)[1]
|
||||
depreciation_date = depreciation_date.replace(day=min(max_day_in_month, month_day))
|
||||
|
||||
# datetime doesn't take into account that the number of days is not the same for each month
|
||||
if not self.prorata and self.method_period % 12 != 0 and self.date_first_depreciation == 'last_day_period':
|
||||
max_day_in_month = calendar.monthrange(depreciation_date.year, depreciation_date.month)[1]
|
||||
depreciation_date = depreciation_date.replace(day=max_day_in_month)
|
||||
|
||||
self.write({'depreciation_line_ids': commands})
|
||||
|
||||
return True
|
||||
|
||||
def validate(self):
|
||||
self.write({'state': 'open'})
|
||||
fields = [
|
||||
'method',
|
||||
'method_number',
|
||||
'method_period',
|
||||
'method_end',
|
||||
'method_progress_factor',
|
||||
'method_time',
|
||||
'salvage_value',
|
||||
'invoice_id',
|
||||
]
|
||||
ref_tracked_fields = self.env['account.asset.asset'].fields_get(fields)
|
||||
for asset in self:
|
||||
tracked_fields = ref_tracked_fields.copy()
|
||||
if asset.method == 'linear':
|
||||
del(tracked_fields['method_progress_factor'])
|
||||
if asset.method_time != 'end':
|
||||
del(tracked_fields['method_end'])
|
||||
else:
|
||||
del(tracked_fields['method_number'])
|
||||
dummy, tracking_value_ids = asset._mail_track(tracked_fields, dict.fromkeys(fields))
|
||||
asset.message_post(subject=_('Asset created'), tracking_value_ids=tracking_value_ids)
|
||||
|
||||
def _return_disposal_view(self, move_ids):
|
||||
name = _('Disposal Move')
|
||||
view_mode = 'form'
|
||||
if len(move_ids) > 1:
|
||||
name = _('Disposal Moves')
|
||||
view_mode = 'tree,form'
|
||||
return {
|
||||
'name': name,
|
||||
'view_type': 'form',
|
||||
'view_mode': view_mode,
|
||||
'res_model': 'account.move',
|
||||
'type': 'ir.actions.act_window',
|
||||
'target': 'current',
|
||||
'res_id': move_ids[0],
|
||||
}
|
||||
|
||||
def _get_disposal_moves(self):
|
||||
move_ids = []
|
||||
for asset in self:
|
||||
unposted_depreciation_line_ids = asset.depreciation_line_ids.filtered(lambda x: not x.move_check)
|
||||
if unposted_depreciation_line_ids:
|
||||
old_values = {
|
||||
'method_end': asset.method_end,
|
||||
'method_number': asset.method_number,
|
||||
}
|
||||
|
||||
# Remove all unposted depr. lines
|
||||
commands = [(2, line_id.id, False) for line_id in unposted_depreciation_line_ids]
|
||||
|
||||
# Create a new depr. line with the residual amount and post it
|
||||
sequence = len(asset.depreciation_line_ids) - len(unposted_depreciation_line_ids) + 1
|
||||
today = fields.Datetime.today()
|
||||
vals = {
|
||||
'amount': asset.value_residual,
|
||||
'asset_id': asset.id,
|
||||
'sequence': sequence,
|
||||
'name': (asset.code or '') + '/' + str(sequence),
|
||||
'remaining_value': 0,
|
||||
'depreciated_value': asset.value - asset.salvage_value, # the asset is completely depreciated
|
||||
'depreciation_date': today,
|
||||
}
|
||||
commands.append((0, False, vals))
|
||||
asset.write({'depreciation_line_ids': commands, 'method_end': today, 'method_number': sequence})
|
||||
tracked_fields = self.env['account.asset.asset'].fields_get(['method_number', 'method_end'])
|
||||
changes, tracking_value_ids = asset._mail_track(tracked_fields, old_values)
|
||||
if changes:
|
||||
asset.message_post(subject=_('Asset sold or disposed. Accounting entry awaiting for validation.'), tracking_value_ids=tracking_value_ids)
|
||||
move_ids += asset.depreciation_line_ids[-1].create_move(post_move=False)
|
||||
|
||||
return move_ids
|
||||
|
||||
def set_to_close(self):
|
||||
move_ids = self._get_disposal_moves()
|
||||
if move_ids:
|
||||
return self._return_disposal_view(move_ids)
|
||||
# Fallback, as if we just clicked on the smartbutton
|
||||
return self.open_entries()
|
||||
|
||||
def set_to_draft(self):
|
||||
self.write({'state': 'draft'})
|
||||
|
||||
@api.depends('value', 'salvage_value', 'depreciation_line_ids.move_check', 'depreciation_line_ids.amount')
|
||||
def _amount_residual(self):
|
||||
for rec in self:
|
||||
total_amount = 0.0
|
||||
for line in rec.depreciation_line_ids:
|
||||
if line.move_check:
|
||||
total_amount += line.amount
|
||||
rec.value_residual = rec.value - total_amount - rec.salvage_value
|
||||
|
||||
@api.onchange('company_id')
|
||||
def onchange_company_id(self):
|
||||
self.currency_id = self.company_id.currency_id.id
|
||||
|
||||
@api.onchange('date_first_depreciation')
|
||||
def onchange_date_first_depreciation(self):
|
||||
for record in self:
|
||||
if record.date_first_depreciation == 'manual':
|
||||
record.first_depreciation_manual_date = record.date
|
||||
|
||||
@api.depends('depreciation_line_ids.move_id')
|
||||
def _entry_count(self):
|
||||
for asset in self:
|
||||
res = self.env['account.asset.depreciation.line'].search_count([('asset_id', '=', asset.id), ('move_id', '!=', False)])
|
||||
asset.entry_count = res or 0
|
||||
|
||||
@api.constrains('prorata', 'method_time')
|
||||
def _check_prorata(self):
|
||||
if self.prorata and self.method_time != 'number':
|
||||
raise ValidationError(_('Prorata temporis can be applied only for the "number of depreciations" time method.'))
|
||||
|
||||
@api.onchange('category_id')
|
||||
def onchange_category_id(self):
|
||||
vals = self.onchange_category_id_values(self.category_id.id)
|
||||
# We cannot use 'write' on an object that doesn't exist yet
|
||||
if vals:
|
||||
for k, v in vals['value'].items():
|
||||
setattr(self, k, v)
|
||||
|
||||
def onchange_category_id_values(self, category_id):
|
||||
if category_id:
|
||||
category = self.env['account.asset.category'].browse(category_id)
|
||||
return {
|
||||
'value': {
|
||||
'method': category.method,
|
||||
'method_number': category.method_number,
|
||||
'method_time': category.method_time,
|
||||
'method_period': category.method_period,
|
||||
'method_progress_factor': category.method_progress_factor,
|
||||
'method_end': category.method_end,
|
||||
'prorata': category.prorata,
|
||||
'date_first_depreciation': category.date_first_depreciation,
|
||||
'account_analytic_id': category.account_analytic_id.id,
|
||||
'analytic_distribution': category.analytic_distribution,
|
||||
}
|
||||
}
|
||||
|
||||
@api.onchange('method_time')
|
||||
def onchange_method_time(self):
|
||||
if self.method_time != 'number':
|
||||
self.prorata = False
|
||||
|
||||
def copy_data(self, default=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
default['name'] = self.name + _(' (copy)')
|
||||
return super(AccountAssetAsset, self).copy_data(default)
|
||||
|
||||
def _compute_entries(self, date, group_entries=False):
|
||||
depreciation_ids = self.env['account.asset.depreciation.line'].search([
|
||||
('asset_id', 'in', self.ids), ('depreciation_date', '<=', date),
|
||||
('move_check', '=', False)])
|
||||
if group_entries:
|
||||
return depreciation_ids.create_grouped_move()
|
||||
return depreciation_ids.create_move()
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
assets = super(AccountAssetAsset, self.with_context(mail_create_nolog=True)).create(vals_list)
|
||||
for asset in assets:
|
||||
asset.sudo().compute_depreciation_board()
|
||||
return assets
|
||||
|
||||
def write(self, vals):
|
||||
res = super(AccountAssetAsset, self).write(vals)
|
||||
if 'depreciation_line_ids' not in vals and 'state' not in vals:
|
||||
for rec in self:
|
||||
rec.compute_depreciation_board()
|
||||
return res
|
||||
|
||||
def open_entries(self):
|
||||
move_ids = []
|
||||
for asset in self:
|
||||
for depreciation_line in asset.depreciation_line_ids:
|
||||
if depreciation_line.move_id:
|
||||
move_ids.append(depreciation_line.move_id.id)
|
||||
return {
|
||||
'name': _('Journal Entries'),
|
||||
'view_type': 'form',
|
||||
'view_mode': 'list,form',
|
||||
'res_model': 'account.move',
|
||||
'view_id': False,
|
||||
'type': 'ir.actions.act_window',
|
||||
'domain': [('id', 'in', move_ids)],
|
||||
}
|
||||
|
||||
|
||||
class AccountAssetDepreciationLine(models.Model):
|
||||
_name = 'account.asset.depreciation.line'
|
||||
_description = 'Asset depreciation line'
|
||||
|
||||
name = fields.Char(string='Depreciation Name', required=True, index=True)
|
||||
sequence = fields.Integer(required=True)
|
||||
asset_id = fields.Many2one('account.asset.asset', string='Asset',
|
||||
required=True, ondelete='cascade')
|
||||
parent_state = fields.Selection(related='asset_id.state',
|
||||
string='State of Asset')
|
||||
amount = fields.Monetary(string='Current Depreciation',
|
||||
required=True)
|
||||
remaining_value = fields.Monetary(string='Next Period Depreciation',
|
||||
required=True)
|
||||
depreciated_value = fields.Monetary(string='Cumulative Depreciation',
|
||||
required=True)
|
||||
depreciation_date = fields.Date('Depreciation Date', index=True)
|
||||
move_id = fields.Many2one('account.move', string='Depreciation Entry')
|
||||
move_check = fields.Boolean(compute='_get_move_check', string='Linked',
|
||||
store=True)
|
||||
move_posted_check = fields.Boolean(compute='_get_move_posted_check',
|
||||
string='Posted', store=True)
|
||||
currency_id = fields.Many2one('res.currency', string='Currency',
|
||||
related='asset_id.currency_id',
|
||||
readonly=True)
|
||||
|
||||
@api.depends('move_id')
|
||||
def _get_move_check(self):
|
||||
for line in self:
|
||||
line.move_check = bool(line.move_id)
|
||||
|
||||
@api.depends('move_id.state')
|
||||
def _get_move_posted_check(self):
|
||||
for line in self:
|
||||
line.move_posted_check = True if line.move_id and line.move_id.state == 'posted' else False
|
||||
|
||||
def create_move(self, post_move=True):
|
||||
created_moves = self.env['account.move']
|
||||
for line in self:
|
||||
if line.move_id:
|
||||
raise UserError(_('This depreciation is already linked to a journal entry. Please post or delete it.'))
|
||||
move_vals = self._prepare_move(line)
|
||||
move = self.env['account.move'].create(move_vals)
|
||||
line.write({'move_id': move.id, 'move_check': True})
|
||||
created_moves |= move
|
||||
|
||||
if post_move and created_moves:
|
||||
created_moves.filtered(lambda m: any(m.asset_depreciation_ids.mapped('asset_id.category_id.open_asset'))).action_post()
|
||||
return [x.id for x in created_moves]
|
||||
|
||||
def _prepare_move(self, line):
|
||||
category_id = line.asset_id.category_id
|
||||
analytic_distribution = line.asset_id.analytic_distribution
|
||||
depreciation_date = self.env.context.get('depreciation_date') or line.depreciation_date or fields.Date.context_today(self)
|
||||
company_currency = line.asset_id.company_id.currency_id
|
||||
current_currency = line.asset_id.currency_id
|
||||
prec = company_currency.decimal_places
|
||||
amount = current_currency._convert(
|
||||
line.amount, company_currency, line.asset_id.company_id, depreciation_date)
|
||||
asset_name = line.asset_id.name + ' (%s/%s)' % (line.sequence, len(line.asset_id.depreciation_line_ids))
|
||||
move_line_1 = {
|
||||
'name': asset_name,
|
||||
'account_id': category_id.account_depreciation_id.id,
|
||||
'debit': 0.0 if float_compare(amount, 0.0, precision_digits=prec) > 0 else -amount,
|
||||
'credit': amount if float_compare(amount, 0.0, precision_digits=prec) > 0 else 0.0,
|
||||
'partner_id': line.asset_id.partner_id.id,
|
||||
'analytic_distribution': analytic_distribution,
|
||||
'currency_id': company_currency != current_currency and current_currency.id or company_currency.id,
|
||||
'amount_currency': - 1.0 * line.amount
|
||||
}
|
||||
move_line_2 = {
|
||||
'name': asset_name,
|
||||
'account_id': category_id.account_depreciation_expense_id.id,
|
||||
'credit': 0.0 if float_compare(amount, 0.0, precision_digits=prec) > 0 else -amount,
|
||||
'debit': amount if float_compare(amount, 0.0, precision_digits=prec) > 0 else 0.0,
|
||||
'partner_id': line.asset_id.partner_id.id,
|
||||
'analytic_distribution': analytic_distribution,
|
||||
'currency_id': company_currency != current_currency and current_currency.id or company_currency.id,
|
||||
'amount_currency': line.amount,
|
||||
}
|
||||
move_vals = {
|
||||
'ref': line.asset_id.code,
|
||||
'date': depreciation_date or False,
|
||||
'journal_id': category_id.journal_id.id,
|
||||
'line_ids': [(0, 0, move_line_1), (0, 0, move_line_2)],
|
||||
}
|
||||
return move_vals
|
||||
|
||||
def _prepare_move_grouped(self):
|
||||
asset_id = self[0].asset_id
|
||||
category_id = asset_id.category_id # we can suppose that all lines have the same category
|
||||
account_analytic_id = asset_id.account_analytic_id
|
||||
# analytic_tag_ids = asset_id.analytic_tag_ids
|
||||
analytic_distribution = asset_id.analytic_distribution
|
||||
|
||||
depreciation_date = self.env.context.get('depreciation_date') or fields.Date.context_today(self)
|
||||
amount = 0.0
|
||||
for line in self:
|
||||
# Sum amount of all depreciation lines
|
||||
company_currency = line.asset_id.company_id.currency_id
|
||||
current_currency = line.asset_id.currency_id
|
||||
company = line.asset_id.company_id
|
||||
amount += current_currency._convert(line.amount, company_currency, company, fields.Date.today())
|
||||
|
||||
name = category_id.name + _(' (grouped)')
|
||||
move_line_1 = {
|
||||
'name': name,
|
||||
'account_id': category_id.account_depreciation_id.id,
|
||||
'debit': 0.0,
|
||||
'credit': amount,
|
||||
'journal_id': category_id.journal_id.id,
|
||||
'analytic_distribution': analytic_distribution,
|
||||
}
|
||||
move_line_2 = {
|
||||
'name': name,
|
||||
'account_id': category_id.account_depreciation_expense_id.id,
|
||||
'credit': 0.0,
|
||||
'debit': amount,
|
||||
'journal_id': category_id.journal_id.id,
|
||||
'analytic_distribution': analytic_distribution,
|
||||
}
|
||||
move_vals = {
|
||||
'ref': category_id.name,
|
||||
'date': depreciation_date or False,
|
||||
'journal_id': category_id.journal_id.id,
|
||||
'line_ids': [(0, 0, move_line_1), (0, 0, move_line_2)],
|
||||
}
|
||||
|
||||
return move_vals
|
||||
|
||||
def create_grouped_move(self, post_move=True):
|
||||
if not self.exists():
|
||||
return []
|
||||
|
||||
created_moves = self.env['account.move']
|
||||
move = self.env['account.move'].create(self._prepare_move_grouped())
|
||||
self.write({'move_id': move.id, 'move_check': True})
|
||||
created_moves |= move
|
||||
|
||||
if post_move and created_moves:
|
||||
created_moves.action_post()
|
||||
return [x.id for x in created_moves]
|
||||
|
||||
def post_lines_and_close_asset(self):
|
||||
# we re-evaluate the assets to determine whether we can close them
|
||||
for line in self:
|
||||
line.log_message_when_posted()
|
||||
asset = line.asset_id
|
||||
if asset.currency_id.is_zero(asset.value_residual):
|
||||
asset.message_post(body=_("Document closed."))
|
||||
asset.write({'state': 'close'})
|
||||
|
||||
def log_message_when_posted(self):
|
||||
def _format_message(message_description, tracked_values):
|
||||
message = ''
|
||||
if message_description:
|
||||
message = '<span>%s</span>' % message_description
|
||||
for name, values in tracked_values.items():
|
||||
message += '<div> • <b>%s</b>: ' % name
|
||||
message += '%s</div>' % values
|
||||
return Markup(message)
|
||||
|
||||
for line in self:
|
||||
if line.move_id and line.move_id.state == 'draft':
|
||||
partner_name = line.asset_id.partner_id.name
|
||||
currency_name = line.asset_id.currency_id.name
|
||||
msg_values = {_('Currency'): currency_name, _('Amount'): line.amount}
|
||||
if partner_name:
|
||||
msg_values[_('Partner')] = partner_name
|
||||
msg = _format_message(_('Depreciation line posted.'), msg_values)
|
||||
line.asset_id.message_post(body=msg)
|
||||
|
||||
def unlink(self):
|
||||
for record in self:
|
||||
if record.move_check:
|
||||
if record.asset_id.category_id.type == 'purchase':
|
||||
msg = _("You cannot delete posted depreciation lines.")
|
||||
else:
|
||||
msg = _("You cannot delete posted installment lines.")
|
||||
raise UserError(msg)
|
||||
return super(AccountAssetDepreciationLine, self).unlink()
|
||||
160
addons/om_account_asset/models/account_move.py
Normal file
@@ -0,0 +1,160 @@
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = 'account.move'
|
||||
|
||||
asset_ids = fields.One2many(
|
||||
'account.asset.asset', 'invoice_id', string="Assets"
|
||||
)
|
||||
|
||||
def button_draft(self):
|
||||
res = super(AccountMove, self).button_draft()
|
||||
for move in self:
|
||||
if any(asset_id.state != 'draft' for asset_id in move.asset_ids):
|
||||
raise ValidationError(_(
|
||||
'You cannot reset to draft for an entry having a posted asset'))
|
||||
if move.asset_ids:
|
||||
move.asset_ids.sudo().write({'active': False})
|
||||
for asset in move.asset_ids:
|
||||
asset.sudo().message_post(body=_("Vendor bill cancelled."))
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _refund_cleanup_lines(self, lines):
|
||||
result = super(AccountMove, self)._refund_cleanup_lines(lines)
|
||||
for i, line in enumerate(lines):
|
||||
for name, field in line._fields.items():
|
||||
if name == 'asset_category_id':
|
||||
result[i][2][name] = False
|
||||
break
|
||||
return result
|
||||
|
||||
def action_cancel(self):
|
||||
res = super(AccountMove, self).action_cancel()
|
||||
assets = self.env['account.asset.asset'].sudo().search(
|
||||
[('invoice_id', 'in', self.ids)])
|
||||
if assets:
|
||||
assets.sudo().write({'active': False})
|
||||
for asset in assets:
|
||||
asset.sudo().message_post(body=_("Vendor bill cancelled."))
|
||||
return res
|
||||
|
||||
def action_post(self):
|
||||
result = super(AccountMove, self).action_post()
|
||||
for inv in self:
|
||||
context = dict(self.env.context)
|
||||
context.pop('default_type', None)
|
||||
for mv_line in inv.invoice_line_ids:
|
||||
mv_line.with_context(context).asset_create()
|
||||
return result
|
||||
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
_inherit = 'account.move.line'
|
||||
|
||||
asset_category_id = fields.Many2one(
|
||||
'account.asset.category', string='Asset Category'
|
||||
)
|
||||
asset_start_date = fields.Date(
|
||||
string='Asset Start Date', compute='_get_asset_date',
|
||||
readonly=True, store=True
|
||||
)
|
||||
asset_end_date = fields.Date(
|
||||
string='Asset End Date', compute='_get_asset_date',
|
||||
readonly=True, store=True
|
||||
)
|
||||
asset_mrr = fields.Float(
|
||||
string='Monthly Recurring Revenue', compute='_get_asset_date',
|
||||
readonly=True, store=True
|
||||
)
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
res = super(AccountMoveLine, self).default_get(fields)
|
||||
if self.env.context.get('create_bill') and not self.asset_category_id:
|
||||
if self.product_id and self.move_id.move_type == 'out_invoice' and \
|
||||
self.product_id.product_tmpl_id.deferred_revenue_category_id:
|
||||
self.asset_category_id = self.product_id.product_tmpl_id.deferred_revenue_category_id.id
|
||||
elif self.product_id and self.product_id.product_tmpl_id.asset_category_id and \
|
||||
self.move_id.move_type == 'in_invoice':
|
||||
self.asset_category_id = self.product_id.product_tmpl_id.asset_category_id.id
|
||||
self.onchange_asset_category_id()
|
||||
return res
|
||||
|
||||
@api.depends('asset_category_id', 'move_id.invoice_date')
|
||||
def _get_asset_date(self):
|
||||
for rec in self:
|
||||
rec.asset_mrr = 0
|
||||
rec.asset_start_date = False
|
||||
rec.asset_end_date = False
|
||||
cat = rec.asset_category_id
|
||||
if cat:
|
||||
if cat.method_number == 0 or cat.method_period == 0:
|
||||
raise UserError(_('The number of depreciations or the period length of '
|
||||
'your asset category cannot be 0.'))
|
||||
months = cat.method_number * cat.method_period
|
||||
if rec.move_id.move_type in ['out_invoice', 'out_refund']:
|
||||
price_subtotal = self.currency_id._convert(
|
||||
self.price_subtotal,
|
||||
self.company_currency_id,
|
||||
self.company_id,
|
||||
self.move_id.invoice_date or fields.Date.context_today(
|
||||
self))
|
||||
|
||||
rec.asset_mrr = price_subtotal / months
|
||||
if rec.move_id.invoice_date:
|
||||
start_date = rec.move_id.invoice_date.replace(day=1)
|
||||
end_date = (start_date + relativedelta(months=months, days=-1))
|
||||
rec.asset_start_date = start_date
|
||||
rec.asset_end_date = end_date
|
||||
|
||||
def asset_create(self):
|
||||
if self.asset_category_id:
|
||||
price_subtotal = self.currency_id._convert(
|
||||
self.price_subtotal,
|
||||
self.company_currency_id,
|
||||
self.company_id,
|
||||
self.move_id.invoice_date or fields.Date.context_today(
|
||||
self))
|
||||
vals = {
|
||||
'name': self.name,
|
||||
'code': self.name or False,
|
||||
'category_id': self.asset_category_id.id,
|
||||
'value': price_subtotal,
|
||||
'partner_id': self.move_id.partner_id.id,
|
||||
'company_id': self.move_id.company_id.id,
|
||||
'currency_id': self.move_id.company_currency_id.id,
|
||||
'date': self.move_id.invoice_date or self.move_id.date,
|
||||
'invoice_id': self.move_id.id,
|
||||
}
|
||||
changed_vals = self.env['account.asset.asset'].onchange_category_id_values(vals['category_id'])
|
||||
vals.update(changed_vals['value'])
|
||||
asset = self.env['account.asset.asset'].create(vals)
|
||||
if self.asset_category_id.open_asset:
|
||||
if asset.date_first_depreciation == 'manual':
|
||||
asset.first_depreciation_manual_date = asset.date
|
||||
asset.validate()
|
||||
return True
|
||||
|
||||
@api.onchange('asset_category_id', 'product_uom_id')
|
||||
def onchange_asset_category_id(self):
|
||||
if self.move_id.move_type == 'out_invoice' and self.asset_category_id:
|
||||
self.account_id = self.asset_category_id.account_asset_id.id
|
||||
elif self.move_id.move_type == 'in_invoice' and self.asset_category_id:
|
||||
self.account_id = self.asset_category_id.account_asset_id.id
|
||||
|
||||
@api.onchange('product_id')
|
||||
def _inverse_product_id(self):
|
||||
res = super(AccountMoveLine, self)._inverse_product_id()
|
||||
for rec in self:
|
||||
if rec.product_id:
|
||||
if rec.move_id.move_type == 'out_invoice':
|
||||
rec.asset_category_id = rec.product_id.product_tmpl_id.deferred_revenue_category_id.id
|
||||
elif rec.move_id.move_type == 'in_invoice':
|
||||
rec.asset_category_id = rec.product_id.product_tmpl_id.asset_category_id.id
|
||||
|
||||
def get_invoice_line_account(self, type, product, fpos, company):
|
||||
return product.asset_category_id.account_asset_id or super(AccountMoveLine, self).get_invoice_line_account(type, product, fpos, company)
|
||||
22
addons/om_account_asset/models/product.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
_inherit = 'product.template'
|
||||
|
||||
asset_category_id = fields.Many2one(
|
||||
'account.asset.category', string='Asset Type',
|
||||
company_dependent=True, ondelete="restrict"
|
||||
)
|
||||
deferred_revenue_category_id = fields.Many2one(
|
||||
'account.asset.category', string='Deferred Revenue Type',
|
||||
company_dependent=True, ondelete="restrict"
|
||||
)
|
||||
|
||||
def _get_asset_accounts(self):
|
||||
res = super(ProductTemplate, self)._get_asset_accounts()
|
||||
if self.asset_category_id:
|
||||
res['stock_input'] = self.property_account_expense_id
|
||||
if self.deferred_revenue_category_id:
|
||||
res['stock_output'] = self.property_account_income_id
|
||||
return res
|
||||
1
addons/om_account_asset/report/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import account_asset_report
|
||||
65
addons/om_account_asset/report/account_asset_report.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from odoo import api, fields, models, tools
|
||||
|
||||
|
||||
class AssetAssetReport(models.Model):
|
||||
_name = "asset.asset.report"
|
||||
_description = "Assets Analysis"
|
||||
_auto = False
|
||||
|
||||
name = fields.Char(string='Year', required=False, readonly=True)
|
||||
date = fields.Date(readonly=True)
|
||||
depreciation_date = fields.Date(string='Depreciation Date', readonly=True)
|
||||
asset_id = fields.Many2one('account.asset.asset', string='Asset', readonly=True)
|
||||
asset_category_id = fields.Many2one('account.asset.category', string='Asset category', readonly=True)
|
||||
partner_id = fields.Many2one('res.partner', string='Partner', readonly=True)
|
||||
state = fields.Selection([('draft', 'Draft'), ('open', 'Running'), ('close', 'Close')], string='Status', readonly=True)
|
||||
depreciation_value = fields.Float(string='Amount of Depreciation Lines', readonly=True)
|
||||
installment_value = fields.Float(string='Amount of Installment Lines', readonly=True)
|
||||
move_check = fields.Boolean(string='Posted', readonly=True)
|
||||
installment_nbr = fields.Integer(string='Installment Count', readonly=True)
|
||||
depreciation_nbr = fields.Integer(string='Depreciation Count', readonly=True)
|
||||
gross_value = fields.Float(string='Gross Amount', readonly=True)
|
||||
posted_value = fields.Float(string='Posted Amount', readonly=True)
|
||||
unposted_value = fields.Float(string='Unposted Amount', readonly=True)
|
||||
company_id = fields.Many2one('res.company', string='Company', readonly=True)
|
||||
|
||||
def init(self):
|
||||
tools.drop_view_if_exists(self.env.cr, 'asset_asset_report')
|
||||
self.env.cr.execute("""
|
||||
create or replace view asset_asset_report as (
|
||||
select
|
||||
min(dl.id) as id,
|
||||
dl.name as name,
|
||||
dl.depreciation_date as depreciation_date,
|
||||
a.date as date,
|
||||
(CASE WHEN dlmin.id = min(dl.id)
|
||||
THEN a.value
|
||||
ELSE 0
|
||||
END) as gross_value,
|
||||
dl.amount as depreciation_value,
|
||||
dl.amount as installment_value,
|
||||
(CASE WHEN dl.move_check
|
||||
THEN dl.amount
|
||||
ELSE 0
|
||||
END) as posted_value,
|
||||
(CASE WHEN NOT dl.move_check
|
||||
THEN dl.amount
|
||||
ELSE 0
|
||||
END) as unposted_value,
|
||||
dl.asset_id as asset_id,
|
||||
dl.move_check as move_check,
|
||||
a.category_id as asset_category_id,
|
||||
a.partner_id as partner_id,
|
||||
a.state as state,
|
||||
count(dl.*) as installment_nbr,
|
||||
count(dl.*) as depreciation_nbr,
|
||||
a.company_id as company_id
|
||||
from account_asset_depreciation_line dl
|
||||
left join account_asset_asset a on (dl.asset_id=a.id)
|
||||
left join (select min(d.id) as id,ac.id as ac_id from account_asset_depreciation_line as d inner join account_asset_asset as ac ON (ac.id=d.asset_id) group by ac_id) as dlmin on dlmin.ac_id=a.id
|
||||
where a.active is true
|
||||
group by
|
||||
dl.amount,dl.asset_id,dl.depreciation_date,dl.name,
|
||||
a.date, dl.move_check, a.state, a.category_id, a.partner_id, a.company_id,
|
||||
a.value, a.id, a.salvage_value, dlmin.id
|
||||
)""")
|
||||
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="action_account_asset_report_pivot" model="ir.ui.view">
|
||||
<field name="name">asset.asset.report.pivot</field>
|
||||
<field name="model">asset.asset.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<pivot string="Assets Analysis" disable_linking="True">
|
||||
<field name="asset_category_id" type="row"/>
|
||||
<field name="gross_value" type="measure"/>
|
||||
<field name="unposted_value" type="measure"/>
|
||||
</pivot>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_account_asset_report_graph" model="ir.ui.view">
|
||||
<field name="name">asset.asset.report.graph</field>
|
||||
<field name="model">asset.asset.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Assets Analysis">
|
||||
<field name="asset_category_id" type="row"/>
|
||||
<field name="gross_value" type="measure"/>
|
||||
<field name="unposted_value" type="measure"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_asset_asset_report_search" model="ir.ui.view">
|
||||
<field name="name">asset.asset.report.search</field>
|
||||
<field name="model">asset.asset.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Assets Analysis">
|
||||
<field name="date"/>
|
||||
<field name="depreciation_date"/>
|
||||
<filter string="Draft" name="draft" domain="[('state','=','draft')]"
|
||||
help="Assets in draft state"/>
|
||||
<filter string="Running" name="running" domain="[('state','=','open')]"
|
||||
help="Assets in running state"/>
|
||||
<separator/>
|
||||
<filter string="Posted" name="posted" domain="[('move_check','=',True)]"
|
||||
help="Posted depreciation lines" context="{'unposted_value_visible': 0}"/>
|
||||
<field name="asset_id"/>
|
||||
<field name="asset_category_id"/>
|
||||
<group>
|
||||
<field name="partner_id" filter_domain="[('partner_id','child_of',self)]"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<group>
|
||||
<filter string="Asset" name="asset" context="{'group_by':'asset_id'}"/>
|
||||
<filter string="Asset Category" name="asset_category"
|
||||
context="{'group_by':'asset_category_id'}"/>
|
||||
<filter string="Company" name="company" context="{'group_by':'company_id'}"
|
||||
groups="base.group_multi_company"/>
|
||||
<separator/>
|
||||
<filter string="Purchase Month" name="purchase_month" help="Date of asset purchase"
|
||||
context="{'group_by':'date:month'}"/>
|
||||
<filter string="Depreciation Month" name="deprecation_month" help="Date of depreciation"
|
||||
context="{'group_by':'depreciation_date:month'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_asset_asset_report" model="ir.actions.act_window">
|
||||
<field name="name">Assets Analysis</field>
|
||||
<field name="res_model">asset.asset.report</field>
|
||||
<field name="view_mode">graph,pivot</field>
|
||||
<field name="search_view_id" ref="view_asset_asset_report_search"/>
|
||||
<field name="domain">[('asset_category_id.type', '=', 'purchase')]</field>
|
||||
<field name="context">{}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_empty_folder">
|
||||
No content
|
||||
</p><p>
|
||||
From this report, you can have an overview on all depreciations. The
|
||||
search bar can also be used to personalize your assets depreciation reporting.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_action_asset_asset_report"
|
||||
name="Assets"
|
||||
action="action_asset_asset_report"
|
||||
parent="account.account_reports_management_menu"
|
||||
sequence="21"/>
|
||||
|
||||
</odoo>
|
||||
21
addons/om_account_asset/security/account_asset_security.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="account_asset_category_multi_company_rule" model="ir.rule">
|
||||
<field name="name">Account Asset Category multi-company</field>
|
||||
<field ref="model_account_asset_category" name="model_id"/>
|
||||
<field eval="True" name="global"/>
|
||||
<field name="domain_force">['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
|
||||
<record id="account_asset_asset_multi_company_rule" model="ir.rule">
|
||||
<field name="name">Account Asset multi-company</field>
|
||||
<field ref="model_account_asset_asset" name="model_id"/>
|
||||
<field eval="True" name="global"/>
|
||||
<field name="domain_force">['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
14
addons/om_account_asset/security/ir.model.access.csv
Normal file
@@ -0,0 +1,14 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_account_asset_category,account.asset.category,model_account_asset_category,account.group_account_user,1,0,0,0
|
||||
access_asset_depreciation_confirmation_wizard,access_asset_depreciation_confirmation_wizard,model_asset_depreciation_confirmation_wizard,account.group_account_user,1,1,1,0
|
||||
access_asset_modify,access_asset_modify,model_asset_modify,account.group_account_user,1,1,1,0
|
||||
access_account_asset_asset,account.asset.asset,model_account_asset_asset,account.group_account_user,1,0,0,0
|
||||
access_account_asset_category_manager,account.asset.category,model_account_asset_category,account.group_account_manager,1,1,1,1
|
||||
access_account_asset_asset_manager,account.asset.asset,model_account_asset_asset,account.group_account_manager,1,1,1,1
|
||||
access_account_asset_depreciation_line,account.asset.depreciation.line,model_account_asset_depreciation_line,account.group_account_user,1,0,0,0
|
||||
access_account_asset_depreciation_line_manager,account.asset.depreciation.line,model_account_asset_depreciation_line,account.group_account_manager,1,1,1,1
|
||||
access_asset_asset_report,asset.asset.report,model_asset_asset_report,account.group_account_user,1,0,0,0
|
||||
access_asset_asset_report_manager,asset.asset.report,model_asset_asset_report,account.group_account_manager,1,1,1,1
|
||||
access_account_asset_category_invoicing_payment,account.asset.category,model_account_asset_category,account.group_account_invoice,1,0,0,0
|
||||
access_account_asset_asset_invoicing_payment,account.asset.asset,model_account_asset_asset,account.group_account_invoice,1,0,1,0
|
||||
access_account_asset_depreciation_line_invoicing_payment,account.asset.depreciation.line,model_account_asset_depreciation_line,account.group_account_invoice,1,0,1,0
|
||||
|
BIN
addons/om_account_asset/static/description/asset_types.png
Normal file
|
After Width: | Height: | Size: 126 KiB |
BIN
addons/om_account_asset/static/description/assets.gif
Normal file
|
After Width: | Height: | Size: 1023 KiB |
BIN
addons/om_account_asset/static/description/assets.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
addons/om_account_asset/static/description/icon.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
1
addons/om_account_asset/static/description/icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="70" height="70" viewBox="0 0 70 70"><defs><path id="a" d="M4 0h61c4 0 5 1 5 5v60c0 4-1 5-5 5H4c-3 0-4-1-4-5V5c0-4 1-5 4-5z"/><linearGradient id="c" x1="100%" x2="0%" y1="0%" y2="100%"><stop offset="0%" stop-color="#DA956B"/><stop offset="100%" stop-color="#CC7039"/></linearGradient></defs><g fill="none" fill-rule="evenodd"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><g mask="url(#b)"><path fill="url(#c)" d="M0 0H70V70H0z"/><path fill="#FFF" fill-opacity=".383" d="M4 1h61c2.667 0 4.333.667 5 2V0H0v3c.667-1.333 2-2 4-2z"/><path fill="#393939" d="M4 69c-2 0-4-.146-4-4.09V40.738L18.16 24H52l1 2.045v6.137l-10.585 11.3 10.05 4.09L37.071 69H4z" opacity=".324"/><path fill="#000" fill-opacity=".383" d="M4 69h61c2.667 0 4.333-1 5-3v4H0v-4c.667 2 2 3 4 3z"/><path fill="#000" d="M53 42.084v5.66c0 1.837-1.111 3.34-3.556 3.34h-28c-2.466 0-4.444-1.503-4.444-3.34V28.34c0-1.837 1.978-3.34 4.444-3.34H49c2.444 0 4 1.368 4 3.205v5.88H37c-2.667 0-4 1.857-4 3.957 0 2.1 1.333 4.042 4 4.042h16zm-15-1.39a2.656 2.656 0 0 1-2.667-2.652A2.656 2.656 0 0 1 38 35.39a2.656 2.656 0 0 1 2.667 2.653A2.656 2.656 0 0 1 38 40.695z" opacity=".3"/><path fill="#FFF" d="M53 40.084v5.66c0 1.837-1.111 3.34-3.556 3.34h-28c-2.466 0-4.444-1.503-4.444-3.34V26.34c0-1.837 1.978-3.34 4.444-3.34H49c2.444 0 4 1.368 4 3.205v5.88H37c-2.667 0-4 1.857-4 3.957 0 2.1 1.333 4.042 4 4.042h16zm-15-1.39a2.656 2.656 0 0 1-2.667-2.652A2.656 2.656 0 0 1 38 33.39a2.656 2.656 0 0 1 2.667 2.653A2.656 2.656 0 0 1 38 38.695z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
84
addons/om_account_asset/static/description/index.html
Normal file
@@ -0,0 +1,84 @@
|
||||
<section class="oe_container oe_dark">
|
||||
<div class="col-md-12">
|
||||
<h2 class="oe_slogan" style="font-size: 35px;color:#2C0091"><b>Odoo 19 Asset Management</b></h2>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<div style="text-align:center;">
|
||||
<p class="fa fa-hand-o-right" style="color:CRIMSON;font-size: 25px;">
|
||||
<span style="color:#2dd280;font-size: 15px;">Manage assets owned by a company or a person.</span>
|
||||
</p><br/>
|
||||
<p class="fa fa-hand-o-right" style="color:CRIMSON;font-size: 25px;">
|
||||
<span style="color:#2dd280;font-size: 15px;">Keeps track of depreciation's, and creates corresponding journal entries</span>
|
||||
</p><br/>
|
||||
|
||||
</div>
|
||||
<br/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="oe_container oe_dark">
|
||||
<div class="oe_row oe_spaced">
|
||||
<div class="oe_centeralign oe_websiteonly">
|
||||
<h4 class="oe_slogan"><a href="https://www.youtube.com/watch?v=KudvDOTvx2I" target="_blank" style="color: #FFFFFF !important; border-radius: 0; background-color: #9c676e; border-color: #005ca7; padding: 15px; font-weight: bold;">
|
||||
<i class="fa fa-youtube"></i>
|
||||
Watch on YouTube
|
||||
</a></h4>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h3 class="oe_slogan" style="color:#332c3c;font-size: 28px;">Asset Category</h3>
|
||||
<div class="oe_demo oe_picture oe_screenshot">
|
||||
<img src="asset_types.png">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="oe_container oe_dark">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h3 class="oe_slogan" style="color:#332c3c;font-size: 28px;">Assets</h3>
|
||||
<div class="oe_demo oe_picture oe_screenshot">
|
||||
<img src="assets.png">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<hr style="width: 100%;height: 4px;background: #2C0091;margin: 0px 0px;">
|
||||
<hr style="width: 100%;height: 4px;background: #148963;margin: 0px 0px;">
|
||||
<section class="oe_container oe_dark">
|
||||
<div class="oe_row ">
|
||||
<div class="oe_slogan text-center">
|
||||
<img src="odoo_mates.png"/>
|
||||
<div style="color:#269900;">
|
||||
<h3 style="color:#2C0091;font-size: 25px;">If you need any help or want more features, just contact us:</h3><br>
|
||||
<h3 style="color:#2C0091;font-size: 20px;">Email: <a href="odoomates@gmail.com">odoomates@gmail.com</a> <br></h3>
|
||||
</div>
|
||||
<div class="oe_slogan">
|
||||
<h2>
|
||||
<a target="_blank" href="https://www.facebook.com/odoomate/" target="new">
|
||||
<i class="fa fa-facebook-square" style="font-size:38px;"></i>
|
||||
</a>
|
||||
<a target="_blank" href="https://twitter.com/odoomates/" target="new">
|
||||
<i class="fa fa-twitter" style="font-size:38px;"></i>
|
||||
</a>
|
||||
<a href="#" target="_blank">
|
||||
<i class="fa fa-linkedin" style="font-size:38px;"></i>
|
||||
</a>
|
||||
<a target="_blank" href="https://www.youtube.com/channel/UCVKlUZP7HAhdQgs-9iTJklQ">
|
||||
<i class="fa fa-youtube-play" style="font-size:38px;"></i>
|
||||
</a>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<hr style="width: 100%;height: 4px;background: #148963;margin: 0px 0px;">
|
||||
<hr style="width: 100%;height: 4px;background: #2C0091;margin: 0px 0px;">
|
||||
|
||||
BIN
addons/om_account_asset/static/description/odoo_mates.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
@@ -0,0 +1,9 @@
|
||||
.o_web_client .o_deprec_lines_toggler {
|
||||
color: theme-color('danger');
|
||||
&.o_is_posted {
|
||||
color: theme-color('success');
|
||||
}
|
||||
&.o_unposted {
|
||||
color: theme-color('warning');
|
||||
}
|
||||
}
|
||||
11
addons/om_account_asset/views/account_asset_templates.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="account_assets_scss_backend" model="ir.asset">
|
||||
<field name="name">aAccount Assets SCSS</field>
|
||||
<field name="bundle">web.assets_backend</field>
|
||||
<field name="path">/om_account_asset/static/src/scss/account_asset.scss</field>
|
||||
<field name="sequence" eval="18"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
238
addons/om_account_asset/views/account_asset_views.xml
Normal file
@@ -0,0 +1,238 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_account_asset_asset_form" model="ir.ui.view">
|
||||
<field name="name">account.asset.asset.form</field>
|
||||
<field name="model">account.asset.asset</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Asset">
|
||||
<header>
|
||||
<button name="validate" string="Confirm" type="object" class="oe_highlight" invisible="state != 'draft'"/>
|
||||
<button type="object" name="compute_depreciation_board" string="Compute Depreciation"
|
||||
invisible="state != 'draft'"/>
|
||||
<button name="set_to_close" invisible="state != 'open'" string="Sell or Dispose" type="object"
|
||||
class="oe_highlight"/>
|
||||
<button name="set_to_draft" string="Set to Draft" type="object"
|
||||
invisible="entry_count != 0 or state != 'open'"/>
|
||||
<button name="%(action_asset_modify)d" invisible="state != 'open'" string="Modify Depreciation" type="action"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,open"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button class="oe_stat_button" name="open_entries" type="object" icon="fa-pencil">
|
||||
<field string="Items" name="entry_count" widget="statinfo"/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
<field name="name" placeholder="e.g. Laptop iBook" readonly="state != 'draft'"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="category_id" string="Asset Category"
|
||||
domain="[('type', '=', 'purchase')]"
|
||||
readonly="state != 'draft'"
|
||||
context="{'default_type': 'purchase'}" help="Category of asset"/>
|
||||
<field name="code" readonly="state != 'draft'"/>
|
||||
<field name="date" help="Date of asset" readonly="state != 'draft'"/>
|
||||
<field name="date_first_depreciation" readonly="state != 'draft'"/>
|
||||
<field name="first_depreciation_manual_date"
|
||||
readonly="state != 'draft'"
|
||||
invisible="date_first_depreciation != 'manual'"
|
||||
required="date_first_depreciation == 'manual'"/>
|
||||
<field name="type" invisible="1"/>
|
||||
<field name="account_analytic_id" groups="analytic.group_analytic_accounting"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="currency_id" groups="base.group_multi_currency"
|
||||
readonly="state != 'draft'"/>
|
||||
<field name="company_id" options="{'no_create': True}"
|
||||
readonly="state != 'draft'"
|
||||
groups="base.group_multi_company"/>
|
||||
<field name="value" widget="monetary"
|
||||
readonly="state != 'draft'" help="Gross value of asset"/>
|
||||
<field name="salvage_value" widget="monetary"
|
||||
readonly="state != 'draft'"
|
||||
invisible="type == 'sale'"/>
|
||||
<field name="value_residual" widget="monetary"/>
|
||||
<field name="partner_id" string="Vendor" widget="res_partner_many2one"
|
||||
readonly="state != 'draft'"
|
||||
context="{'res_partner_search_mode': 'supplier'}"/>
|
||||
<field name="invoice_id" string="Invoice" options="{'no_create': True}"/>
|
||||
<field name="analytic_distribution" widget="analytic_distribution"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Depreciation Board">
|
||||
<field name="depreciation_line_ids" mode="list"
|
||||
readonly="state not in ('draft', 'open')"
|
||||
options="{'reload_whole_on_button': true}">
|
||||
<list string="Depreciation Lines" decoration-info="(move_check == False)"
|
||||
create="false">
|
||||
<field name="depreciation_date"/>
|
||||
<field name="amount" widget="monetary" string="Depreciation"/>
|
||||
<field name="depreciated_value" readonly="1"/>
|
||||
<field name="remaining_value" readonly="1" widget="monetary" string="Residual"/>
|
||||
<field name="move_check" widget="deprec_lines_toggler"
|
||||
invisible="parent_state != 'open'"/>
|
||||
<field name="move_posted_check" invisible="1"/>
|
||||
<field name="parent_state" invisible="1"/>
|
||||
</list>
|
||||
<form string="Depreciation Lines" create="false">
|
||||
<group>
|
||||
<group>
|
||||
<field name="parent_state" invisible="1"/>
|
||||
<field name="name"/>
|
||||
<field name="sequence"/>
|
||||
<field name="move_id"/>
|
||||
<field name="move_check"/>
|
||||
<field name="parent_state" invisible="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="amount" widget="monetary"/>
|
||||
<field name="depreciation_date"/>
|
||||
<field name="depreciated_value"/>
|
||||
<field name="remaining_value"/>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Depreciation Information">
|
||||
<group>
|
||||
<field name="method" widget="radio"
|
||||
invisible="type == 'sale'" readonly="state != 'draft'"/>
|
||||
<field name="method_progress_factor"
|
||||
readonly="state != 'draft'"
|
||||
invisible="method == 'linear'"
|
||||
required="method == 'degressive'"/>
|
||||
<field name="method_time" string="Time Method Based On"
|
||||
widget="radio"
|
||||
readonly="state != 'draft'"
|
||||
invisible="type != 'purchase'"/>
|
||||
<field name="prorata"
|
||||
invisible="method_time == 'end'"
|
||||
readonly="state != 'draft'"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="method_number"
|
||||
readonly="state != 'draft'"
|
||||
invisible="method_time == 'end'"
|
||||
required="method_time == 'number'"/>
|
||||
<field name="method_period" readonly="state != 'draft'"/>
|
||||
<field name="method_end"
|
||||
readonly="state != 'draft'"
|
||||
invisible="method_time == 'end'"
|
||||
required="method_time == 'number'"/>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_account_asset_asset_kanban" model="ir.ui.view">
|
||||
<field name="name">account.asset.asset.kanban</field>
|
||||
<field name="model">account.asset.asset</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="o_kanban_mobile">
|
||||
<field name="name"/>
|
||||
<field name="category_id"/>
|
||||
<field name="date"/>
|
||||
<field name="state"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="oe_kanban_global_click">
|
||||
<div class="row mb4">
|
||||
<div class="col-6">
|
||||
<strong>
|
||||
<span>
|
||||
<t t-esc="record.name.value"/>
|
||||
</span>
|
||||
</strong>
|
||||
</div>
|
||||
<div class="col-6 text-end">
|
||||
<strong>
|
||||
<t t-esc="record.date.value"/>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 text-muted">
|
||||
<span>
|
||||
<t t-esc="record.category_id.value"/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<span class="float-right text-end">
|
||||
<field name="state" widget="kanban_label_selection"
|
||||
options="{'classes': {'draft': 'primary', 'open': 'success', 'close': 'default'}}"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_account_asset_asset_purchase_tree" model="ir.ui.view">
|
||||
<field name="name">account.asset.asset.purchase.list</field>
|
||||
<field name="model">account.asset.asset</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Assets" decoration-info="(state == 'draft')" decoration-muted="(state == 'close')">
|
||||
<field name="name"/>
|
||||
<field name="category_id" string="Asset Category"/>
|
||||
<field name="date"/>
|
||||
<field name="partner_id" string="Vendor"/>
|
||||
<field name="value"/>
|
||||
<field name="value_residual" widget="monetary"/>
|
||||
<field name="currency_id" groups="base.group_multi_currency"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="state"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_account_asset_search" model="ir.ui.view">
|
||||
<field name="name">account.asset.asset.search</field>
|
||||
<field name="model">account.asset.asset</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Asset Account">
|
||||
<field name="name" string="Asset"/>
|
||||
<field name="date"/>
|
||||
<filter string="Current" name="current" domain="[('state','in', ('draft','open'))]"
|
||||
help="Assets in draft and open states"/>
|
||||
<filter string="Closed" name="closed" domain="[('state','=', 'close')]"
|
||||
help="Assets in closed state"/>
|
||||
<field name="category_id" string="Asset Category"/>
|
||||
<field name="partner_id" filter_domain="[('partner_id','child_of',self)]"/>
|
||||
<group>
|
||||
<filter string="Date" name="month" domain="[]" context="{'group_by':'date'}"/>
|
||||
<filter string="Asset Category" name="category" domain="[]" context="{'group_by':'category_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_account_asset_asset_form">
|
||||
<field name="name">Assets</field>
|
||||
<field name="res_model">account.asset.asset</field>
|
||||
<field name="view_mode">list,kanban,form</field>
|
||||
<field name="view_id" ref="view_account_asset_asset_purchase_tree"/>
|
||||
<field name="domain">[('category_id.type', '=', 'purchase')]</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_action_account_asset_asset_form"
|
||||
parent="account.account_account_menu"
|
||||
action="action_account_asset_asset_form"
|
||||
sequence="101"
|
||||
groups="account.group_account_manager"/>
|
||||
|
||||
|
||||
</odoo>
|
||||
21
addons/om_account_asset/views/account_move_views.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_invoice_asset_category" model="ir.ui.view">
|
||||
<field name="name">account.move.supplier.form</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="inherit_id" ref="account.view_move_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='invoice_line_ids']/list/field[@name='account_id']" position="before">
|
||||
<field string="Asset Category" name="asset_category_id"
|
||||
force_save="1" column_invisible="parent.move_type != 'in_invoice'"
|
||||
domain="[('type','=','purchase')]" context="{'default_type':'purchase'}"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='line_ids']/list/field[@name='account_id']" position="before">
|
||||
<field string="Asset Category" name="asset_category_id"
|
||||
column_invisible="1"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
176
addons/om_account_asset/views/asset_category_views.xml
Normal file
@@ -0,0 +1,176 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_account_asset_category_form" model="ir.ui.view">
|
||||
<field name="name">account.asset.category.form</field>
|
||||
<field name="model">account.asset.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Asset category">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name" string="Asset Type" class="oe_edit_only"
|
||||
invisible="type != 'purchase'"/>
|
||||
<label for="name" string="Deferred Revenue Type" class="oe_edit_only"
|
||||
invisible="type == 'purchase'"/>
|
||||
<h1>
|
||||
<field name="name" placeholder="e.g. Computers"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group string="Journal Entries">
|
||||
<group>
|
||||
<field name="journal_id"/>
|
||||
<div>
|
||||
<label for="account_asset_id" invisible="type != 'purchase'"
|
||||
style="font-weight: bold" class="o_light_label"/>
|
||||
<label for="account_asset_id" string="Deferred Revenue Account"
|
||||
invisible="type != 'sale'" style="font-weight: bold"
|
||||
class="o_light_label"/>
|
||||
</div>
|
||||
<field name="account_asset_id" nolabel="1"
|
||||
domain="[('company_ids', 'in', company_id)]"/>
|
||||
<div>
|
||||
<label for="account_depreciation_id" invisible="type != 'purchase'"
|
||||
style="font-weight: bold" class="o_light_label"/>
|
||||
<label for="account_depreciation_id" string="Recognition Income Account"
|
||||
invisible="type != 'sale'"
|
||||
style="font-weight: bold" class="o_light_label"/>
|
||||
</div>
|
||||
<field name="account_depreciation_id" nolabel="1"
|
||||
domain="[('company_ids', 'in', company_id)]"/>
|
||||
</group>
|
||||
<group>
|
||||
<div>
|
||||
<label for="account_depreciation_expense_id"
|
||||
invisible="type != 'purchase'"
|
||||
style="font-weight: bold" class="o_light_label"/>
|
||||
<label for="account_depreciation_expense_id" string="Recognition Account"
|
||||
invisible="type != 'sale'"
|
||||
style="font-weight: bold" class="o_light_label"/>
|
||||
</div>
|
||||
<field name="account_depreciation_expense_id" nolabel="1"
|
||||
domain="[('company_ids', 'in', company_id)]"/>
|
||||
<field name="account_analytic_id" domain="[('company_id', 'in', company_id)]"
|
||||
groups="analytic.group_analytic_accounting"/>
|
||||
<field name="analytic_distribution" widget="analytic_distribution"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group string="Periodicity">
|
||||
<field name="method_time" string="Time Method Based On" widget="radio"
|
||||
invisible="type != 'purchase'"/>
|
||||
<field name="method_number" string="Number of Entries"
|
||||
required="method_time == 'number'"
|
||||
invisible="method_time != 'number' or type == False"/>
|
||||
<label for="method_period" string="One Entry Every"/>
|
||||
<div>
|
||||
<field name="method_period" nolabel="1"
|
||||
invisible="type == False" class="oe_inline"/>
|
||||
months
|
||||
</div>
|
||||
<field name="method_end"
|
||||
required="method_time == 'end'"
|
||||
invisible="method_time != 'end'"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="type" invisible="1"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="company_id" options="{'no_create': True}"
|
||||
groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<group string="Additional Options">
|
||||
<field name="open_asset"/>
|
||||
<field name="group_entries"/>
|
||||
<field name="date_first_depreciation"/>
|
||||
</group>
|
||||
<group invisible="type == 'sale'" string="Depreciation Method">
|
||||
<field name="method" widget="radio"/>
|
||||
<field name="method_progress_factor"
|
||||
invisible="method_time == 'linear'" required="method == 'degressive'"/>
|
||||
<field name="prorata" invisible="method_time == 'end'"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_account_asset_asset_category_kanban" model="ir.ui.view">
|
||||
<field name="name">account.asset.category.kanban</field>
|
||||
<field name="model">account.asset.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="o_kanban_mobile">
|
||||
<field name="name"/>
|
||||
<field name="journal_id"/>
|
||||
<field name="method"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="oe_kanban_card oe_kanban_global_click">
|
||||
<div class="row mb4">
|
||||
<div class="col-6">
|
||||
<strong>
|
||||
<span>
|
||||
<t t-esc="record.name.value"/>
|
||||
</span>
|
||||
</strong>
|
||||
</div>
|
||||
<div class="col-6 text-end">
|
||||
<span class="badge badge-pill">
|
||||
<strong>
|
||||
<t t-esc="record.method.value"/>
|
||||
</strong>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<t t-esc="record.journal_id.value"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_account_asset_category_tree" model="ir.ui.view">
|
||||
<field name="name">account.asset.category.list</field>
|
||||
<field name="model">account.asset.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Asset category">
|
||||
<field name="name"/>
|
||||
<field name="journal_id"/>
|
||||
<field name="method"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_account_asset_category_search" model="ir.ui.view">
|
||||
<field name="name">account.asset.category.search</field>
|
||||
<field name="model">account.asset.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Asset Category">
|
||||
<filter string="Sales" name="sales" domain="[('type','=', 'sale')]" help="Deferred Revenues"/>
|
||||
<filter string="Purchase" name="purchase" domain="[('type','=', 'purchase')]" help="Assets"/>
|
||||
<field name="name" string="Category"/>
|
||||
<field name="journal_id"/>
|
||||
<group>
|
||||
<filter string="Type" name="type" domain="[]" context="{'group_by':'type'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_account_asset_asset_list_normal_purchase" model="ir.actions.act_window">
|
||||
<field name="name">Asset Category</field>
|
||||
<field name="res_model">account.asset.category</field>
|
||||
<field name="domain">[('type', '=', 'purchase')]</field>
|
||||
<field name="view_mode">list,kanban,form</field>
|
||||
<field name="context">{'default_type': 'purchase'}</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_action_account_asset_asset_list_normal_purchase"
|
||||
parent="account.account_account_menu"
|
||||
action="action_account_asset_asset_list_normal_purchase"
|
||||
sequence="6"/>
|
||||
|
||||
</odoo>
|
||||
18
addons/om_account_asset/views/product_views.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_product_template_form_inherit" model="ir.ui.view">
|
||||
<field name="name">Product Template (form)</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="account.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="property_account_expense_id" position="after">
|
||||
<field name="asset_category_id"
|
||||
domain="[('type', '=', 'purchase')]"
|
||||
context="{'default_type': 'purchase'}"
|
||||
groups="account.group_account_user"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
4
addons/om_account_asset/wizard/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import asset_depreciation_confirmation_wizard
|
||||
from . import asset_modify
|
||||
@@ -0,0 +1,29 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
class AssetDepreciationConfirmationWizard(models.TransientModel):
|
||||
_name = "asset.depreciation.confirmation.wizard"
|
||||
_description = "asset.depreciation.confirmation.wizard"
|
||||
|
||||
date = fields.Date(
|
||||
'Account Date', required=True,
|
||||
help="Choose the period for which you want to automatically post the depreciation lines of running assets",
|
||||
default=fields.Date.context_today
|
||||
)
|
||||
|
||||
def asset_compute(self):
|
||||
self.ensure_one()
|
||||
context = self.env.context
|
||||
created_move_ids = self.env['account.asset.asset'].compute_generated_entries(self.date, asset_type=context.get('asset_type'))
|
||||
|
||||
return {
|
||||
'name': _('Created Asset Moves') if context.get('asset_type') == 'purchase' else _('Created Revenue Moves'),
|
||||
'view_type': 'form',
|
||||
'view_mode': 'list,form',
|
||||
'res_model': 'account.move',
|
||||
'view_id': False,
|
||||
'domain': "[('id','in',[" + ','.join(str(id) for id in created_move_ids) + "])]",
|
||||
'type': 'ir.actions.act_window',
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_asset_depreciation_confirmation_wizard" model="ir.ui.view">
|
||||
<field name="name">asset.depreciation.confirmation.wizard</field>
|
||||
<field name="model">asset.depreciation.confirmation.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Compute Asset">
|
||||
<div>
|
||||
<p>
|
||||
This wizard will post installment/depreciation lines for the selected month.<br/>
|
||||
This will generate journal entries for all related installment lines on this period
|
||||
of asset/revenue recognition as well.
|
||||
</p>
|
||||
</div>
|
||||
<group>
|
||||
<field name="date"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button string="Generate Entries" name="asset_compute" type="object" class="btn-primary"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_asset_depreciation_confirmation_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Post Depreciation Lines</field>
|
||||
<field name="res_model">asset.depreciation.confirmation.wizard</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="view_id" ref="view_asset_depreciation_confirmation_wizard"/>
|
||||
<field name="target">new</field>
|
||||
<field name="context">{'asset_type': 'purchase'}</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_finance_entries_generate_entries"
|
||||
parent="account.menu_finance_entries"
|
||||
name="Generate Entries"/>
|
||||
|
||||
|
||||
<menuitem id="menu_asset_depreciation_confirmation_wizard"
|
||||
name="Generate Assets Entries"
|
||||
action="action_asset_depreciation_confirmation_wizard"
|
||||
parent="om_account_asset.menu_finance_entries_generate_entries"
|
||||
sequence="111"
|
||||
groups="account.group_account_manager"/>
|
||||
|
||||
</odoo>
|
||||
67
addons/om_account_asset/wizard/asset_modify.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class AssetModify(models.TransientModel):
|
||||
_name = 'asset.modify'
|
||||
_description = 'Modify Asset'
|
||||
|
||||
name = fields.Text(string='Reason', required=True)
|
||||
method_number = fields.Integer(
|
||||
string='Number of Depreciation', required=True
|
||||
)
|
||||
method_period = fields.Integer(string='Period Length')
|
||||
method_end = fields.Date(string='Ending date')
|
||||
asset_method_time = fields.Char(
|
||||
compute='_get_asset_method_time', string='Asset Method Time', readonly=True
|
||||
)
|
||||
|
||||
def _get_asset_method_time(self):
|
||||
if self.env.context.get('active_id'):
|
||||
asset = self.env['account.asset.asset'].browse(self.env.context.get('active_id'))
|
||||
self.asset_method_time = asset.method_time
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
res = super(AssetModify, self).default_get(fields)
|
||||
asset_id = self.env.context.get('active_id')
|
||||
asset = self.env['account.asset.asset'].browse(asset_id)
|
||||
if 'name' in fields:
|
||||
res.update({'name': asset.name})
|
||||
if 'method_number' in fields and asset.method_time == 'number':
|
||||
res.update({'method_number': asset.method_number})
|
||||
if 'method_period' in fields:
|
||||
res.update({'method_period': asset.method_period})
|
||||
if 'method_end' in fields and asset.method_time == 'end':
|
||||
res.update({'method_end': asset.method_end})
|
||||
if self.env.context.get('active_id'):
|
||||
active_asset = self.env['account.asset.asset'].browse(self.env.context.get('active_id'))
|
||||
res['asset_method_time'] = active_asset.method_time
|
||||
return res
|
||||
|
||||
def modify(self):
|
||||
""" Modifies the duration of asset for calculating depreciation
|
||||
and maintains the history of old values, in the chatter.
|
||||
"""
|
||||
asset_id = self.env.context.get('active_id', False)
|
||||
asset = self.env['account.asset.asset'].browse(asset_id)
|
||||
old_values = {
|
||||
'method_number': asset.method_number,
|
||||
'method_period': asset.method_period,
|
||||
'method_end': asset.method_end,
|
||||
}
|
||||
asset_vals = {
|
||||
'method_number': self.method_number,
|
||||
'method_period': self.method_period,
|
||||
'method_end': self.method_end,
|
||||
}
|
||||
if asset_vals['method_number'] <= asset.entry_count:
|
||||
raise UserError(_('The number of depreciations must be greater than the number of posted or draft entries '
|
||||
'to allow for complete depreciation of the asset.'))
|
||||
asset.write(asset_vals)
|
||||
asset.compute_depreciation_board()
|
||||
tracked_fields = self.env['account.asset.asset'].fields_get(['method_number', 'method_period', 'method_end'])
|
||||
changes, tracking_value_ids = asset._mail_track(tracked_fields, old_values)
|
||||
if changes:
|
||||
asset.message_post(subject=_('Depreciation board modified'), body=self.name, tracking_value_ids=tracking_value_ids)
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
40
addons/om_account_asset/wizard/asset_modify_views.xml
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="asset_modify_form" model="ir.ui.view">
|
||||
<field name="name">wizard.asset.modify.form</field>
|
||||
<field name="model">asset.modify</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Modify Asset">
|
||||
<field name="asset_method_time" invisible="1"/>
|
||||
<group string="Asset Durations to Modify" col="4">
|
||||
<group colspan="2" col="2">
|
||||
<field name="name"/>
|
||||
<field name="method_number" invisible="asset_method_time == 'end'"/>
|
||||
</group>
|
||||
<group colspan="2" col="2">
|
||||
<field name="method_end" invisible="asset_method_time == 'number'"/>
|
||||
<label for="method_period"/>
|
||||
<div>
|
||||
<field name="method_period" class="oe_inline"/> months
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="modify" string="Modify" type="object" class="btn-primary"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_asset_modify" model="ir.actions.act_window">
|
||||
<field name="name">Modify Asset</field>
|
||||
<field name="res_model">asset.modify</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="view_id" ref="asset_modify_form"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
45
addons/om_account_followup/README.rst
Normal file
@@ -0,0 +1,45 @@
|
||||
=============================
|
||||
Customer Follow Up Management
|
||||
=============================
|
||||
|
||||
This Module will add customer follow up management in Odoo 19 Community Edition
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
To install this module, you need to:
|
||||
|
||||
Download the module and add it to your Odoo addons folder. Afterward, log on to
|
||||
your Odoo server and go to the Apps menu. Trigger the debug mode and update the
|
||||
list by clicking on the "Update Apps List" link. Now install the module by
|
||||
clicking on the install button.
|
||||
|
||||
Upgrade
|
||||
============
|
||||
|
||||
To upgrade this module, you need to:
|
||||
|
||||
Download the module and add it to your Odoo addons folder. Restart the server
|
||||
and log on to your Odoo server. Select the Apps menu and upgrade the module by
|
||||
clicking on the upgrade button.
|
||||
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Configure follow up levels
|
||||
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
* Odoo Mates <odoomates@gmail.com>
|
||||
|
||||
|
||||
Author & Maintainer
|
||||
-------------------
|
||||
|
||||
This module is maintained by the Odoo Mates
|
||||
3
addons/om_account_followup/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from . import wizard
|
||||
from . import models
|
||||
from . import report
|
||||
27
addons/om_account_followup/__manifest__.py
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
'name': 'Customer Follow Up Management',
|
||||
'version': '19.0.1.0.2', # __odoosky_original_version__: '1.0.2'
|
||||
'category': 'Accounting',
|
||||
'description': """Customer FollowUp Management""",
|
||||
'summary': """Customer FollowUp Management""",
|
||||
'author': 'Odoo Mates, Odoo S.A',
|
||||
'license': 'LGPL-3',
|
||||
'website': 'https://www.odoomates.tech',
|
||||
'depends': ['account', 'mail'],
|
||||
'data': [
|
||||
'security/security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'data/mail_template_data.xml',
|
||||
'wizard/followup_print_view.xml',
|
||||
'wizard/followup_results_view.xml',
|
||||
'views/followup_view.xml',
|
||||
'views/account_move.xml',
|
||||
'views/partners.xml',
|
||||
'views/report_followup.xml',
|
||||
'views/reports.xml',
|
||||
'views/followup_partner_view.xml',
|
||||
'report/followup_report.xml',
|
||||
],
|
||||
'demo': ['demo/demo.xml'],
|
||||
'images': ['static/description/banner.png'],
|
||||
}
|
||||
242
addons/om_account_followup/data/mail_template_data.xml
Normal file
@@ -0,0 +1,242 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="email_template_om_account_followup_level0" model="mail.template">
|
||||
<field name="name">First polite payment follow-up reminder email</field>
|
||||
<field name="email_from">{{ (user.email or '') }}</field>
|
||||
<field name="subject">{{ user.company_id.name }} Payment Reminder</field>
|
||||
<field name="lang">{{ object.lang }}</field>
|
||||
<field name="model_id" ref="base.model_res_partner"/>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
<field name="body_html"><![CDATA[
|
||||
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); ">
|
||||
|
||||
<p>Dear <t t-out="object.name"/>,</p>
|
||||
<p>
|
||||
Exception made if there was a mistake of ours, it seems that the following amount stays unpaid. Please, take
|
||||
appropriate measures in order to carry out this payment in the next 8 days.
|
||||
|
||||
Would your payment have been carried out after this mail was sent, please ignore this message. Do not hesitate to
|
||||
contact our accounting department.
|
||||
|
||||
</p>
|
||||
<br/>
|
||||
Best Regards,
|
||||
<br/>
|
||||
<br/>
|
||||
<t t-out="user.name" />
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<t t-out="object.get_followup_table_html()" />
|
||||
|
||||
<br/>
|
||||
|
||||
</div>
|
||||
]]></field>
|
||||
</record>
|
||||
|
||||
<!--Mail template level 1 -->
|
||||
<record id="email_template_om_account_followup_level1" model="mail.template">
|
||||
<field name="name">A bit urging second payment follow-up reminder email</field>
|
||||
<field name="email_from">{{ (user.email or '') }}</field>
|
||||
<field name="subject">{{ user.company_id.name }} Payment Reminder</field>
|
||||
<field name="email_to">{{ object.email }}</field>
|
||||
<field name="lang">{{ object.lang }}</field>
|
||||
<field name="model_id" ref="base.model_res_partner"/>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
<field name="body_html"><![CDATA[
|
||||
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); ">
|
||||
|
||||
<p>Dear <t t-out="object.name"/>,</p>
|
||||
<p>
|
||||
We are disappointed to see that despite sending a reminder, that your account is now seriously overdue.
|
||||
It is essential that immediate payment is made, otherwise we will have to consider placing a stop on your account
|
||||
which means that we will no longer be able to supply your company with (goods/services).
|
||||
Please, take appropriate measures in order to carry out this payment in the next 8 days.
|
||||
If there is a problem with paying invoice that we are not aware of, do not hesitate to contact our accounting
|
||||
department. so that we can resolve the matter quickly.
|
||||
Details of due payments is printed below.
|
||||
</p>
|
||||
<br/>
|
||||
Best Regards,
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<t t-out="user.name" />
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<t t-out="object.get_followup_table_html()" />
|
||||
|
||||
<br/>
|
||||
|
||||
</div>
|
||||
]]></field>
|
||||
</record>
|
||||
|
||||
<!--Mail template level 2 -->
|
||||
<record id="email_template_om_account_followup_level2" model="mail.template">
|
||||
<field name="name">Urging payment follow-up reminder email</field>
|
||||
<field name="email_from">{{ (user.email or '') }}</field>
|
||||
<field name="subject">{{ user.company_id.name }} Payment Reminder</field>
|
||||
<field name="email_to">{{ object.email }}</field>
|
||||
<field name="lang">{{ object.lang }}</field>
|
||||
<field name="model_id" ref="base.model_res_partner"/>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
<field name="body_html"><![CDATA[
|
||||
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); ">
|
||||
|
||||
<p>Dear <t t-out="object.name"/>,</p>
|
||||
<p>
|
||||
Despite several reminders, your account is still not settled.
|
||||
Unless full payment is made in next 8 days, legal action for the recovery of the debt will be taken without
|
||||
further notice.
|
||||
I trust that this action will prove unnecessary and details of due payments is printed below.
|
||||
In case of any queries concerning this matter, do not hesitate to contact our accounting department.
|
||||
</p>
|
||||
<br/>
|
||||
Best Regards,
|
||||
<br/>
|
||||
<br/>
|
||||
<t t-out="user.name" />
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<t t-out="object.get_followup_table_html()" />
|
||||
|
||||
<br/>
|
||||
|
||||
</div>
|
||||
]]></field>
|
||||
</record>
|
||||
|
||||
<!-- Default follow up message -->
|
||||
<record id="email_template_om_account_followup_default" model="mail.template">
|
||||
<field name="name">Default payment follow-up reminder e-mail</field>
|
||||
<field name="email_from">{{ (user.email or '') }}</field>
|
||||
<field name="subject">{{ user.company_id.name }} Payment Reminder</field>
|
||||
<field name="email_to">{{ object.email }}</field>
|
||||
<field name="lang">{{ object.lang }}</field>
|
||||
<field name="model_id" ref="base.model_res_partner"/>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
<field name="body_html"><![CDATA[
|
||||
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); ">
|
||||
|
||||
<p>Dear <t t-out="object.name"/>,</p>
|
||||
<p>
|
||||
Exception made if there was a mistake of ours, it seems that the following amount stays unpaid. Please, take
|
||||
appropriate measures in order to carry out this payment in the next 8 days.
|
||||
Would your payment have been carried out after this mail was sent, please ignore this message. Do not hesitate to
|
||||
contact our accounting department.
|
||||
</p>
|
||||
<br/>
|
||||
Best Regards,
|
||||
<br/>
|
||||
<br/>
|
||||
<t t-out="user.name" />
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<t t-out="object.get_followup_table_html()" />
|
||||
|
||||
<br/>
|
||||
</div>
|
||||
]]></field>
|
||||
</record>
|
||||
|
||||
<record id="demo_followup1" model="followup.followup" forcecreate="False">
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
</record>
|
||||
|
||||
<record id="demo_followup_line1" model="followup.line" forcecreate="False">
|
||||
<field name="name">Send first reminder email</field>
|
||||
<field name="sequence">0</field>
|
||||
<field name="delay">15</field>
|
||||
<field name="followup_id" ref="demo_followup1"/>
|
||||
<field name="send_email">True</field>
|
||||
<field name="description">
|
||||
Dear %(partner_name)s,
|
||||
|
||||
Exception made if there was a mistake of ours, it seems that
|
||||
the following amount stays unpaid. Please, take appropriate
|
||||
measures in order to carry out this payment in the next 8 days.
|
||||
|
||||
Would your payment have been carried out after this mail was
|
||||
sent, please ignore this message. Do not hesitate to contact
|
||||
our accounting department.
|
||||
|
||||
Best Regards,
|
||||
</field>
|
||||
<field name="email_template_id" ref="email_template_om_account_followup_level0"/>
|
||||
</record>
|
||||
|
||||
<record id="demo_followup_line2" model="followup.line" forcecreate="False">
|
||||
<field name="name">Send reminder letter and email</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="delay">30</field>
|
||||
<field name="followup_id" ref="demo_followup1"/>
|
||||
<field name="email_template_id"
|
||||
ref="email_template_om_account_followup_level1"/>
|
||||
<field name="send_email">True</field>
|
||||
<field name="send_letter">True</field>
|
||||
<field name="description">
|
||||
Dear %(partner_name)s,
|
||||
|
||||
We are disappointed to see that despite sending a reminder,
|
||||
that your account is now seriously overdue.
|
||||
|
||||
It is essential that immediate payment is made, otherwise we
|
||||
will have to consider placing a stop on your account which
|
||||
means that we will no longer be able to supply your company
|
||||
with (goods/services).
|
||||
Please, take appropriate measures in order to carry out this
|
||||
payment in the next 8 days.
|
||||
|
||||
If there is a problem with paying invoice that we are not aware
|
||||
of, do not hesitate to contact our accounting department, so
|
||||
that we can resolve the matter quickly.
|
||||
|
||||
Details of due payments is printed below.
|
||||
|
||||
Best Regards,
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="demo_followup_line3" model="followup.line" forcecreate="False">
|
||||
<field name="name">Call the customer on the phone</field>
|
||||
<field name="sequence">3</field>
|
||||
<field name="delay">40</field>
|
||||
<field name="followup_id" ref="demo_followup1"/>
|
||||
<field name="email_template_id"
|
||||
ref="email_template_om_account_followup_level2"/>
|
||||
<field eval="False" name="send_email"/>
|
||||
<field name="manual_action">True</field>
|
||||
<field name="manual_action_note">Call the customer on the phone!</field>
|
||||
<field name="description">
|
||||
Dear %(partner_name)s,
|
||||
|
||||
Despite several reminders, your account is still not settled.
|
||||
|
||||
Unless full payment is made in next 8 days, then legal action
|
||||
for the recovery of the debt will be taken without further
|
||||
notice.
|
||||
|
||||
I trust that this action will prove unnecessary and details of
|
||||
due payments is printed below.
|
||||
|
||||
In case of any queries concerning this matter, do not hesitate
|
||||
to contact our accounting department.
|
||||
|
||||
Best Regards,
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
59
addons/om_account_followup/demo/demo.xml
Normal file
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="demo_followup_line4" model="followup.line">
|
||||
<field name="name">Urging reminder email</field>
|
||||
<field name="sequence">4</field>
|
||||
<field name="delay">50</field>
|
||||
<field name="followup_id" ref="demo_followup1"/>
|
||||
<field name="send_email">True</field>
|
||||
<field name="email_template_id" ref="email_template_om_account_followup_level2"/>
|
||||
<field name="description">
|
||||
Dear %(partner_name)s,
|
||||
|
||||
Despite several reminders, your account is still not settled.
|
||||
|
||||
Unless full payment is made in next 8 days, then legal action
|
||||
for the recovery of the debt will be taken without further
|
||||
notice.
|
||||
|
||||
I trust that this action will prove unnecessary and details of
|
||||
due payments is printed below.
|
||||
|
||||
In case of any queries concerning this matter, do not hesitate
|
||||
to contact our accounting department.
|
||||
|
||||
Best Regards,
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="demo_followup_line5" model="followup.line">
|
||||
<field name="name">Urging reminder letter</field>
|
||||
<field name="sequence">5</field>
|
||||
<field name="delay">60</field>
|
||||
<field name="followup_id" ref="demo_followup1"/>
|
||||
<field eval="False" name="send_email"/>
|
||||
<field name="send_letter">True</field>
|
||||
<field name="email_template_id" ref="email_template_om_account_followup_level2"/>
|
||||
<field name="description">
|
||||
Dear %(partner_name)s,
|
||||
|
||||
Despite several reminders, your account is still not settled.
|
||||
|
||||
Unless full payment is made in next 8 days, then legal action
|
||||
for the recovery of the debt will be taken without further
|
||||
notice.
|
||||
|
||||
I trust that this action will prove unnecessary and details of
|
||||
due payments is printed below.
|
||||
|
||||
In case of any queries concerning this matter, do not hesitate
|
||||
to contact our accounting department.
|
||||
|
||||
Best Regards,
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
1313
addons/om_account_followup/i18n/ar_001.po
Normal file
1307
addons/om_account_followup/i18n/ar_SY.po
Normal file
1496
addons/om_account_followup/i18n/de.po
Normal file
1337
addons/om_account_followup/i18n/es_AR.po
Normal file
1501
addons/om_account_followup/i18n/fr.po
Normal file
1361
addons/om_account_followup/i18n/tr.po
Normal file
1304
addons/om_account_followup/i18n/uk.po
Normal file
1541
addons/om_account_followup/i18n/zh_TW.po
Normal file
7
addons/om_account_followup/models/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import account_move
|
||||
from . import followup
|
||||
from . import followup_partner
|
||||
from . import partner
|
||||
from . import settings
|
||||
13
addons/om_account_followup/models/account_move.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
_inherit = 'account.move.line'
|
||||
|
||||
followup_line_id = fields.Many2one('followup.line', 'Follow-up Level')
|
||||
followup_date = fields.Date('Latest Follow-up')
|
||||
result = fields.Float(compute='_get_result', string="Balance Amount")
|
||||
|
||||
def _get_result(self):
|
||||
for aml in self:
|
||||
aml.result = aml.debit - aml.credit
|
||||
91
addons/om_account_followup/models/followup.py
Normal file
@@ -0,0 +1,91 @@
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class FollowupFollowup(models.Model):
|
||||
_name = 'followup.followup'
|
||||
_description = 'Account Follow-up'
|
||||
_rec_name = 'name'
|
||||
|
||||
name = fields.Char(string="Name", related='company_id.name', readonly=True)
|
||||
followup_line = fields.One2many('followup.line', 'followup_id', 'Follow-up', copy=True)
|
||||
company_id = fields.Many2one('res.company', 'Company', required=True, default=lambda self: self.env.company)
|
||||
|
||||
_company_uniq = models.Constraint(
|
||||
'unique(company_id)',
|
||||
'Only one follow-up per company is allowed',
|
||||
)
|
||||
|
||||
|
||||
class FollowupLine(models.Model):
|
||||
_name = 'followup.line'
|
||||
_description = 'Follow-up Criteria'
|
||||
_order = 'delay'
|
||||
|
||||
def _compute_sequence(self):
|
||||
delays = [line.delay for line in self.followup_id.followup_line]
|
||||
delays.sort()
|
||||
for line in self.followup_id.followup_line:
|
||||
sequence = delays.index(line.delay)
|
||||
line.sequence = sequence+1
|
||||
|
||||
@api.model
|
||||
def default_get(self, default_fields):
|
||||
values = super(FollowupLine, self).default_get(default_fields)
|
||||
if self.env.ref('om_account_followup.email_template_om_account_followup_default'):
|
||||
values['email_template_id'] = self.env.ref('om_account_followup.email_template_om_account_followup_default').id
|
||||
return values
|
||||
|
||||
name = fields.Char('Follow-Up Action', required=True)
|
||||
sequence = fields.Integer('Sequence', compute='_compute_sequence',
|
||||
store=False,
|
||||
help="Gives the sequence order when displaying a list of follow-up lines.")
|
||||
followup_id = fields.Many2one('followup.followup', 'Follow Ups',
|
||||
required=True, ondelete="cascade")
|
||||
delay = fields.Integer('Due Days',
|
||||
help="The number of days after the due date of the "
|
||||
"invoice to wait before sending the reminder. Could be negative if you want "
|
||||
"to send a polite alert beforehand.",
|
||||
required=True)
|
||||
description = fields.Text('Printed Message', translate=True, default="""
|
||||
Dear %(partner_name)s,
|
||||
|
||||
Exception made if there was a mistake of ours, it seems that the following
|
||||
amount stays unpaid. Please, take appropriate measures in order to carry out
|
||||
this payment in the next 8 days.
|
||||
|
||||
Would your payment have been carried out after this mail was sent, please
|
||||
ignore this message. Do not hesitate to contact our accounting department.
|
||||
|
||||
Best Regards,
|
||||
""", )
|
||||
send_email = fields.Boolean('Send an Email', default=True,
|
||||
help="When processing, it will send an email")
|
||||
send_letter = fields.Boolean('Send a Letter', default=True,
|
||||
help="When processing, it will print a letter")
|
||||
manual_action = fields.Boolean('Manual Action', default=False,
|
||||
help="When processing, it will set the "
|
||||
"manual action to be taken for that customer. ")
|
||||
manual_action_note = fields.Text('Action To Do')
|
||||
manual_action_responsible_id = fields.Many2one('res.users',
|
||||
string='Assign a Responsible', ondelete='set null')
|
||||
email_template_id = fields.Many2one('mail.template', 'Email Template',
|
||||
ondelete='set null')
|
||||
|
||||
_days_uniq = models.Constraint(
|
||||
'unique(followup_id, delay)',
|
||||
'Days of the follow-up levels must be different',
|
||||
)
|
||||
|
||||
@api.constrains('description')
|
||||
def _check_description(self):
|
||||
for line in self:
|
||||
if line.description:
|
||||
try:
|
||||
line.description % {'partner_name': '', 'date': '',
|
||||
'user_signature': '',
|
||||
'company_name': ''}
|
||||
except ValidationError:
|
||||
raise ValidationError(
|
||||
_('Your description is invalid, use the right legend '
|
||||
'or %% if you want to use the percent character.'))
|
||||
50
addons/om_account_followup/models/followup_partner.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from odoo import api, fields, models, _
|
||||
from odoo import tools
|
||||
|
||||
|
||||
class FollowupStatByPartner(models.Model):
|
||||
_name = "followup.stat.by.partner"
|
||||
_description = "Follow-up Statistics by Partner"
|
||||
_rec_name = 'partner_id'
|
||||
_auto = False
|
||||
|
||||
def _get_invoice_partner_id(self):
|
||||
for rec in self:
|
||||
rec.invoice_partner_id = rec.partner_id.address_get(
|
||||
adr_pref=['invoice']).get('invoice', rec.partner_id.id)
|
||||
|
||||
partner_id = fields.Many2one('res.partner', 'Partner', readonly=True)
|
||||
date_move = fields.Date('First move', readonly=True)
|
||||
date_move_last = fields.Date('Last move', readonly=True)
|
||||
date_followup = fields.Date('Latest follow-up', readonly=True)
|
||||
max_followup_id = fields.Many2one('followup.line', 'Max Follow Up Level', readonly=True, ondelete="cascade")
|
||||
balance = fields.Float('Balance', readonly=True)
|
||||
company_id = fields.Many2one('res.company', 'Company', readonly=True)
|
||||
invoice_partner_id = fields.Many2one('res.partner', compute='_get_invoice_partner_id', string='Invoice Address')
|
||||
|
||||
@api.model
|
||||
def init(self):
|
||||
tools.drop_view_if_exists(self.env.cr, 'followup_stat_by_partner')
|
||||
self.env.cr.execute("""
|
||||
create view followup_stat_by_partner as (
|
||||
SELECT
|
||||
l.partner_id * 10000::bigint + l.company_id as id,
|
||||
l.partner_id AS partner_id,
|
||||
min(l.date) AS date_move,
|
||||
max(l.date) AS date_move_last,
|
||||
max(l.followup_date) AS date_followup,
|
||||
max(l.followup_line_id) AS max_followup_id,
|
||||
sum(l.debit - l.credit) AS balance,
|
||||
l.company_id as company_id
|
||||
FROM
|
||||
account_move_line l
|
||||
LEFT JOIN account_account a ON (l.account_id = a.id)
|
||||
WHERE
|
||||
a.account_type = 'asset_receivable' AND
|
||||
l.full_reconcile_id is NULL AND
|
||||
l.partner_id IS NOT NULL
|
||||
GROUP BY
|
||||
l.partner_id, l.company_id
|
||||
)""")
|
||||
|
||||
|
||||
408
addons/om_account_followup/models/partner.py
Normal file
@@ -0,0 +1,408 @@
|
||||
from functools import reduce
|
||||
from lxml import etree
|
||||
from odoo import api, fields, models, _
|
||||
from datetime import datetime
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools.misc import formatLang
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_inherit = "res.partner"
|
||||
|
||||
def fields_view_get(self, view_id=None, view_type='form', toolbar=False,
|
||||
submenu=False):
|
||||
res = super(ResPartner, self).fields_view_get(
|
||||
view_id=view_id, view_type=view_type, toolbar=toolbar,
|
||||
submenu=submenu)
|
||||
if view_type == 'form' and self.env.context.get('Followupfirst'):
|
||||
doc = etree.XML(res['arch'], parser=None, base_url=None)
|
||||
first_node = doc.xpath("//page[@name='followup_tab']")
|
||||
root = first_node[0].getparent()
|
||||
root.insert(0, first_node[0])
|
||||
res['arch'] = etree.tostring(doc, encoding="utf-8")
|
||||
return res
|
||||
|
||||
def _get_latest(self):
|
||||
company = self.env.user.company_id
|
||||
for partner in self:
|
||||
amls = partner.unreconciled_aml_ids
|
||||
latest_date = False
|
||||
latest_level = False
|
||||
latest_days = False
|
||||
latest_level_without_lit = False
|
||||
latest_days_without_lit = False
|
||||
for aml in amls:
|
||||
aml_followup = aml.followup_line_id
|
||||
if (aml.company_id == company) and aml_followup and \
|
||||
(not latest_days or latest_days < aml_followup.delay):
|
||||
latest_days = aml_followup.delay
|
||||
latest_level = aml_followup.id
|
||||
if (aml.company_id == company) and aml.followup_date and (
|
||||
not latest_date or latest_date < aml.followup_date):
|
||||
latest_date = aml.followup_date
|
||||
if (aml.company_id == company) and \
|
||||
(aml_followup and (not latest_days_without_lit or
|
||||
latest_days_without_lit < aml_followup.delay)):
|
||||
latest_days_without_lit = aml_followup.delay
|
||||
latest_level_without_lit = aml_followup.id
|
||||
partner.latest_followup_date = latest_date
|
||||
partner.latest_followup_level_id = latest_level
|
||||
partner.latest_followup_level_id_without_lit = latest_level_without_lit
|
||||
|
||||
def do_partner_manual_action_dermanord(self, followup_line):
|
||||
action_text = followup_line.manual_action_note or ''
|
||||
|
||||
action_date = self.payment_next_action_date or \
|
||||
fields.Date.today()
|
||||
if self.payment_responsible_id:
|
||||
responsible_id = self.payment_responsible_id.id
|
||||
else:
|
||||
p = followup_line.manual_action_responsible_id
|
||||
responsible_id = p and p.id or False
|
||||
self.write({'payment_next_action_date': action_date,
|
||||
'payment_next_action': action_text,
|
||||
'payment_responsible_id': responsible_id})
|
||||
|
||||
def do_partner_manual_action(self, partner_ids):
|
||||
for partner in self.browse(partner_ids):
|
||||
followup_without_lit = partner.latest_followup_level_id_without_lit
|
||||
if partner.payment_next_action:
|
||||
action_text = \
|
||||
(partner.payment_next_action or '') + "\n" + \
|
||||
(followup_without_lit.manual_action_note or '')
|
||||
else:
|
||||
action_text = followup_without_lit.manual_action_note or ''
|
||||
|
||||
action_date = partner.payment_next_action_date or \
|
||||
fields.Date.today()
|
||||
|
||||
if partner.payment_responsible_id:
|
||||
responsible_id = partner.payment_responsible_id.id
|
||||
else:
|
||||
p = followup_without_lit.manual_action_responsible_id
|
||||
responsible_id = p and p.id or False
|
||||
partner.write({'payment_next_action_date': action_date,
|
||||
'payment_next_action': action_text,
|
||||
'payment_responsible_id': responsible_id})
|
||||
|
||||
def do_partner_print(self, wizard_partner_ids, data):
|
||||
if not wizard_partner_ids:
|
||||
return {}
|
||||
data['partner_ids'] = wizard_partner_ids
|
||||
datas = {
|
||||
'ids': wizard_partner_ids,
|
||||
'model': 'followup.followup',
|
||||
'form': data
|
||||
}
|
||||
return self.env.ref(
|
||||
'om_account_followup.action_report_followup').report_action(
|
||||
self, data=datas)
|
||||
|
||||
def do_partner_mail(self):
|
||||
ctx = self.env.context.copy()
|
||||
ctx['followup'] = True
|
||||
template = 'om_account_followup.email_template_om_account_followup_default'
|
||||
unknown_mails = 0
|
||||
for partner in self:
|
||||
partners_to_email = [child for child in partner.child_ids if
|
||||
child.type == 'invoice' and child.email]
|
||||
if not partners_to_email and partner.email:
|
||||
partners_to_email = [partner]
|
||||
if partners_to_email:
|
||||
level = partner.latest_followup_level_id_without_lit
|
||||
for partner_to_email in partners_to_email:
|
||||
if level and level.send_email and \
|
||||
level.email_template_id and \
|
||||
level.email_template_id.id:
|
||||
level.email_template_id.with_context(ctx).send_mail(
|
||||
partner_to_email.id)
|
||||
else:
|
||||
mail_template_id = self.env.ref(template)
|
||||
mail_template_id.with_context(ctx).send_mail(
|
||||
partner_to_email.id)
|
||||
if partner not in partners_to_email:
|
||||
partner.message_post(body=_(
|
||||
'Overdue email sent to %s' % ', '.join(
|
||||
['%s <%s>' % (partner.name, partner.email) for
|
||||
partner in partners_to_email])))
|
||||
else:
|
||||
unknown_mails = unknown_mails + 1
|
||||
action_text = _("Email not sent because of email address "
|
||||
"of partner not filled in")
|
||||
if partner.payment_next_action_date:
|
||||
payment_action_date = min(
|
||||
fields.Date.today(),
|
||||
partner.payment_next_action_date)
|
||||
else:
|
||||
payment_action_date = fields.Date.today()
|
||||
if partner.payment_next_action:
|
||||
payment_next_action = \
|
||||
partner.payment_next_action + " \n " + action_text
|
||||
else:
|
||||
payment_next_action = action_text
|
||||
partner.with_context(ctx).write(
|
||||
{'payment_next_action_date': payment_action_date,
|
||||
'payment_next_action': payment_next_action})
|
||||
return unknown_mails
|
||||
|
||||
def get_followup_table_html(self):
|
||||
self.ensure_one()
|
||||
partner = self.commercial_partner_id
|
||||
followup_table = ''
|
||||
if partner.unreconciled_aml_ids:
|
||||
company = self.env.user.company_id
|
||||
current_date = fields.Date.today()
|
||||
report = self.env['report.om_account_followup.report_followup']
|
||||
final_res = report._lines_get_with_partner(partner, company.id)
|
||||
|
||||
for currency_dict in final_res:
|
||||
currency = currency_dict.get('line', [
|
||||
{'currency_id': company.currency_id}])[0]['currency_id']
|
||||
followup_table += '''
|
||||
<table border="2" width=100%%>
|
||||
<tr>
|
||||
<td>''' + _("Invoice Date") + '''</td>
|
||||
<td>''' + _("Description") + '''</td>
|
||||
<td>''' + _("Reference") + '''</td>
|
||||
<td>''' + _("Due Date") + '''</td>
|
||||
<td>''' + _("Amount") + " (%s)" % (
|
||||
currency.symbol) + '''</td>
|
||||
<td>''' + _("Lit.") + '''</td>
|
||||
</tr>
|
||||
'''
|
||||
total = 0
|
||||
for aml in currency_dict['line']:
|
||||
total += aml['balance']
|
||||
strbegin = "<TD>"
|
||||
strend = "</TD>"
|
||||
date = aml['date_maturity'] or aml['date']
|
||||
date = datetime.strptime(date, "%m/%d/%Y").date()
|
||||
if date <= current_date and aml['balance'] > 0:
|
||||
strbegin = "<TD><B>"
|
||||
strend = "</B></TD>"
|
||||
followup_table += "<TR>" + strbegin + str(aml['date']) + \
|
||||
strend + strbegin + aml['name'] + \
|
||||
strend + strbegin + \
|
||||
(aml['ref'] or '') + strend + \
|
||||
strbegin + str(date) + strend + \
|
||||
strbegin + str(aml['balance']) + \
|
||||
strend + "</TR>"
|
||||
|
||||
total = reduce(lambda x, y: x + y['balance'],
|
||||
currency_dict['line'], 0.00)
|
||||
total = formatLang(self.env, total, currency_obj=currency)
|
||||
followup_table += '''<tr> </tr>
|
||||
</table>
|
||||
<center>''' + _(
|
||||
"Amount due") + ''' : %s </center>''' % (total)
|
||||
return followup_table
|
||||
|
||||
def write(self, vals):
|
||||
if vals.get("payment_responsible_id", False):
|
||||
for part in self:
|
||||
if part.payment_responsible_id != \
|
||||
self.env['res.users'].browse(vals["payment_responsible_id"]):
|
||||
# Find partner_id of user put as responsible
|
||||
responsible_partner_id = self.env["res.users"].browse(
|
||||
vals['payment_responsible_id']).partner_id.id
|
||||
part.message_post(
|
||||
body=_("You became responsible to do the next action "
|
||||
"for the payment follow-up of") +
|
||||
" <b><a href='#id=" + str(part.id) +
|
||||
"&view_type=form&model=res.partner'> " + part.name +
|
||||
" </a></b>",
|
||||
type='comment',
|
||||
context=self.env.context,
|
||||
partner_ids=[responsible_partner_id])
|
||||
return super(ResPartner, self).write(vals)
|
||||
|
||||
def action_done(self):
|
||||
return self.write({'payment_next_action_date': False,
|
||||
'payment_next_action': '',
|
||||
'payment_responsible_id': False})
|
||||
|
||||
def do_button_print(self):
|
||||
self.ensure_one()
|
||||
company_id = self.env.user.company_id.id
|
||||
if not self.env['account.move.line'].search(
|
||||
[('partner_id', '=', self.id),
|
||||
('account_id.account_type', '=', 'asset_receivable'),
|
||||
('full_reconcile_id', '=', False),
|
||||
('company_id', '=', company_id),
|
||||
'|', ('date_maturity', '=', False),
|
||||
('date_maturity', '<=', fields.Date.today())]):
|
||||
raise ValidationError(
|
||||
_("The partner does not have any accounting entries to "
|
||||
"print in the overdue report for the current company."))
|
||||
self.message_post(body=_('Printed overdue payments report'))
|
||||
self.message_post(body=_('Printed overdue payments report'))
|
||||
|
||||
wizard_partner_ids = [self.id * 10000 + company_id]
|
||||
followup_ids = self.env['followup.followup'].search(
|
||||
[('company_id', '=', company_id)])
|
||||
if not followup_ids:
|
||||
raise ValidationError(_(
|
||||
"There is no followup plan defined for the current company."))
|
||||
data = {
|
||||
'date': fields.date.today(),
|
||||
'followup_id': followup_ids[0].id,
|
||||
}
|
||||
return self.do_partner_print(wizard_partner_ids, data)
|
||||
|
||||
def _get_amounts_and_date(self):
|
||||
company = self.env.user.company_id
|
||||
current_date = fields.Date.today()
|
||||
for partner in self:
|
||||
worst_due_date = False
|
||||
amount_due = amount_overdue = 0.0
|
||||
for aml in partner.unreconciled_aml_ids:
|
||||
if (aml.company_id == company):
|
||||
date_maturity = aml.date_maturity or aml.date
|
||||
if not worst_due_date or date_maturity < worst_due_date:
|
||||
worst_due_date = date_maturity
|
||||
amount_due += aml.result
|
||||
if (date_maturity <= current_date):
|
||||
amount_overdue += aml.result
|
||||
partner.payment_amount_due = amount_due
|
||||
partner.payment_amount_overdue = amount_overdue
|
||||
partner.payment_earliest_due_date = worst_due_date
|
||||
|
||||
def _get_followup_overdue_query(self, args, overdue_only=False):
|
||||
company_id = self.env.user.company_id.id
|
||||
having_clauses = []
|
||||
having_values = []
|
||||
|
||||
for field, operator, value in args:
|
||||
if operator in ['=', '!=', '>', '>=', '<', '<=']:
|
||||
having_clauses.append(f'SUM(bal2) {operator} %s')
|
||||
having_values.append(value)
|
||||
else:
|
||||
raise ValueError(f"Unsupported operator: {operator}")
|
||||
|
||||
having_where_clause = ' AND '.join(having_clauses)
|
||||
overdue_only_str = 'AND date_maturity <= NOW()' if overdue_only else ''
|
||||
|
||||
query = ('''
|
||||
SELECT pid AS partner_id, SUM(bal2) FROM (
|
||||
SELECT
|
||||
CASE WHEN bal IS NOT NULL THEN bal ELSE 0.0 END AS bal2,
|
||||
p.id as pid
|
||||
FROM (
|
||||
SELECT
|
||||
(debit - credit) AS bal,
|
||||
partner_id
|
||||
FROM account_move_line l
|
||||
LEFT JOIN account_account a ON a.id = l.account_id
|
||||
WHERE a.account_type = 'asset_receivable'
|
||||
%s AND full_reconcile_id IS NULL
|
||||
AND l.company_id = %%s
|
||||
) AS l
|
||||
RIGHT JOIN res_partner p ON p.id = partner_id
|
||||
) AS pl
|
||||
GROUP BY pid HAVING %s
|
||||
''') % (overdue_only_str, having_where_clause)
|
||||
|
||||
params = [company_id] + having_values
|
||||
return query, params
|
||||
|
||||
def _payment_overdue_search(self, operator, operand):
|
||||
args = [('payment_amount_overdue', operator, operand)]
|
||||
query, params = self._get_followup_overdue_query(args, overdue_only=True)
|
||||
self.env.cr.execute(query, params)
|
||||
res = self.env.cr.fetchall()
|
||||
if not res:
|
||||
return [('id', '=', '0')]
|
||||
return [('id', 'in', [x[0] for x in res])]
|
||||
|
||||
def _payment_earliest_date_search(self, operator, operand):
|
||||
args = [('payment_earliest_due_date', operator, operand)]
|
||||
company_id = self.env.user.company_id.id
|
||||
having_where_clause = ' AND '.join(
|
||||
map(lambda x: "(MIN(l.date_maturity) %s '%%s')" % (x[1]), args))
|
||||
having_values = [x[2] for x in args]
|
||||
having_where_clause = having_where_clause % (having_values[0])
|
||||
query = """SELECT partner_id FROM account_move_line l
|
||||
LEFT JOIN account_account a ON a.id = l.account_id
|
||||
WHERE a.account_type = 'asset_receivable'
|
||||
AND l.company_id = %s
|
||||
AND l.full_reconcile_id IS NULL
|
||||
AND partner_id IS NOT NULL GROUP BY partner_id"""
|
||||
query = query % company_id
|
||||
if having_where_clause:
|
||||
query += ' HAVING %s ' % (having_where_clause)
|
||||
self.env.cr.execute(query)
|
||||
res = self.env.cr.fetchall()
|
||||
if not res:
|
||||
return [('id', '=', '0')]
|
||||
return [('id', 'in', [x[0] for x in res])]
|
||||
|
||||
def _payment_due_search(self, operator, operand):
|
||||
args = [('payment_amount_due', operator, operand)]
|
||||
query, params = self._get_followup_overdue_query(args, overdue_only=False)
|
||||
self.env.cr.execute(query, params)
|
||||
res = self.env.cr.fetchall()
|
||||
if not res:
|
||||
return [('id', '=', '0')]
|
||||
return [('id', 'in', [x[0] for x in res])]
|
||||
|
||||
def _get_partners(self):
|
||||
partners = set()
|
||||
for aml in self:
|
||||
if aml.partner_id:
|
||||
partners.add(aml.partner_id.id)
|
||||
return list(partners)
|
||||
|
||||
payment_responsible_id = fields.Many2one(
|
||||
'res.users', ondelete='set null',
|
||||
string='Follow-up Responsible', tracking=True, copy=False,
|
||||
help="Optionally you can assign a user to this field, which will make "
|
||||
"him responsible for the action.")
|
||||
payment_note = fields.Text(
|
||||
'Customer Payment Promise', help="Payment Note", copy=False
|
||||
)
|
||||
payment_next_action = fields.Text(
|
||||
'Next Action', copy=False, tracking=True,
|
||||
help="This is the next action to be taken. It will automatically be "
|
||||
"set when the partner gets a follow-up level that requires a manual action. "
|
||||
)
|
||||
payment_next_action_date = fields.Date(
|
||||
'Next Action Date', copy=False,
|
||||
help="This is when the manual follow-up is needed. The date will be "
|
||||
"set to the current date when the partner gets a follow-up level "
|
||||
"that requires a manual action. Can be practical to set manually "
|
||||
"e.g. to see if he keeps his promises."
|
||||
)
|
||||
unreconciled_aml_ids = fields.One2many(
|
||||
'account.move.line', 'partner_id',
|
||||
domain=[('full_reconcile_id', '=', False), ('account_id.account_type', '=', 'asset_receivable')]
|
||||
)
|
||||
latest_followup_date = fields.Date(
|
||||
compute='_get_latest', string="Latest Follow-up Date", compute_sudo=True,
|
||||
help="Latest date that the follow-up level of the partner was changed"
|
||||
)
|
||||
latest_followup_level_id = fields.Many2one(
|
||||
'followup.line', compute='_get_latest', compute_sudo=True,
|
||||
string="Latest Follow-up Level", help="The maximum follow-up level"
|
||||
)
|
||||
|
||||
latest_followup_sequence = fields.Integer(
|
||||
'Sequence',
|
||||
help="Gives the sequence order when displaying a list of follow-up lines.", default=0
|
||||
)
|
||||
latest_followup_level_id_without_lit = fields.Many2one(
|
||||
'followup.line', compute='_get_latest', compute_sudo=True,
|
||||
string="Latest Follow-up Level without litigation",
|
||||
help="The maximum follow-up level without taking into "
|
||||
"account the account move lines with litigation")
|
||||
payment_amount_due = fields.Float(
|
||||
compute='_get_amounts_and_date',
|
||||
string="Amount Due", search='_payment_due_search'
|
||||
)
|
||||
payment_amount_overdue = fields.Float(
|
||||
compute='_get_amounts_and_date',
|
||||
string="Amount Overdue", search='_payment_overdue_search'
|
||||
)
|
||||
payment_earliest_due_date = fields.Date(
|
||||
compute='_get_amounts_and_date', string="Worst Due Date",
|
||||
search='_payment_earliest_date_search'
|
||||
)
|
||||
15
addons/om_account_followup/models/settings.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
class AccountConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
def open_followup_level_form(self):
|
||||
res_ids = self.env['followup.followup'].search([], limit=1)
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': 'Follow-up Levels',
|
||||
'res_model': 'followup.followup',
|
||||
'res_id': res_ids and res_ids.id or False,
|
||||
'view_mode': 'form,list',
|
||||
}
|
||||
2
addons/om_account_followup/report/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from . import followup_print
|
||||
from . import followup_report
|
||||
110
addons/om_account_followup/report/followup_print.py
Normal file
@@ -0,0 +1,110 @@
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.tools import format_date
|
||||
|
||||
|
||||
class ReportFollowup(models.AbstractModel):
|
||||
_name = 'report.om_account_followup.report_followup'
|
||||
_description = 'Report Followup'
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
model = self.env['followup.sending.results']
|
||||
ids = self.env.context.get('active_ids') or False
|
||||
docs = model.browse(ids)
|
||||
return {
|
||||
'docs': docs,
|
||||
'doc_ids': docids,
|
||||
'doc_model': model,
|
||||
'time': time,
|
||||
'ids_to_objects': self._ids_to_objects,
|
||||
'getLines': self._lines_get,
|
||||
'get_text': self._get_text,
|
||||
'data': data and data['form'] or {}}
|
||||
|
||||
def _ids_to_objects(self, ids):
|
||||
all_lines = []
|
||||
for line in self.env['followup.stat.by.partner'].browse(ids):
|
||||
if line not in all_lines:
|
||||
all_lines.append(line)
|
||||
return all_lines
|
||||
|
||||
def _lines_get(self, stat_by_partner_line):
|
||||
return self._lines_get_with_partner(stat_by_partner_line.partner_id,
|
||||
stat_by_partner_line.company_id.id)
|
||||
|
||||
def _lines_get_with_partner(self, partner, company_id):
|
||||
moveline_obj = self.env['account.move.line']
|
||||
moveline_ids = moveline_obj.search(
|
||||
[('partner_id', '=', partner.id),
|
||||
('account_id.account_type', '=', 'asset_receivable'),
|
||||
('full_reconcile_id', '=', False),
|
||||
('company_id', '=', company_id),
|
||||
'|', ('date_maturity', '=', False),
|
||||
('date_maturity', '<=', fields.Date.today())])
|
||||
lines_per_currency = defaultdict(list)
|
||||
total = 0
|
||||
for line in moveline_ids:
|
||||
currency = line.currency_id or line.company_id.currency_id
|
||||
balance = line.debit - line.credit
|
||||
if currency != line.company_id.currency_id:
|
||||
balance = line.amount_currency
|
||||
line_data = {
|
||||
'name': line.move_id.name,
|
||||
'ref': line.ref,
|
||||
'date': format_date(self.env, line.date),
|
||||
'date_maturity': format_date(self.env, line.date_maturity),
|
||||
'balance': balance,
|
||||
'currency_id': currency,
|
||||
}
|
||||
total = total + line_data['balance']
|
||||
lines_per_currency[currency].append(line_data)
|
||||
|
||||
return [{'total': total, 'line': lines, 'currency': currency} for
|
||||
currency, lines in
|
||||
lines_per_currency.items()]
|
||||
|
||||
def _get_text(self, stat_line, followup_id, context=None):
|
||||
fp_obj = self.env['followup.followup']
|
||||
fp_line = fp_obj.browse(followup_id).followup_line
|
||||
if not fp_line:
|
||||
raise ValidationError(
|
||||
_("The followup plan defined for the current company does not "
|
||||
"have any followup action."))
|
||||
default_text = ''
|
||||
li_delay = []
|
||||
for line in fp_line:
|
||||
if not default_text and line.description:
|
||||
default_text = line.description
|
||||
li_delay.append(line.delay)
|
||||
li_delay.sort(reverse=True)
|
||||
partner_line_ids = self.env['account.move.line'].search(
|
||||
[('partner_id', '=', stat_line.partner_id.id),
|
||||
('full_reconcile_id', '=', False),
|
||||
('company_id', '=', stat_line.company_id.id),
|
||||
('debit', '!=', False),
|
||||
('account_id.account_type', '=', 'asset_receivable'),
|
||||
('followup_line_id', '!=', False)])
|
||||
|
||||
partner_max_delay = 0
|
||||
partner_max_text = ''
|
||||
for i in partner_line_ids:
|
||||
if i.followup_line_id.delay > partner_max_delay and \
|
||||
i.followup_line_id.description:
|
||||
partner_max_delay = i.followup_line_id.delay
|
||||
partner_max_text = i.followup_line_id.description
|
||||
text = partner_max_delay and partner_max_text or default_text
|
||||
if text:
|
||||
lang_obj = self.env['res.lang']
|
||||
lang_ids = lang_obj.search(
|
||||
[('code', '=', stat_line.partner_id.lang)], limit=1)
|
||||
date_format = lang_ids and lang_ids.date_format or '%Y-%m-%d'
|
||||
text = text % {
|
||||
'partner_name': stat_line.partner_id.name,
|
||||
'date': time.strftime(date_format),
|
||||
'company_name': stat_line.company_id.name,
|
||||
'user_signature': self.env.user.signature or '',
|
||||
}
|
||||
return text
|
||||
47
addons/om_account_followup/report/followup_report.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from odoo import api, fields, models
|
||||
from odoo import tools
|
||||
|
||||
|
||||
class AccountFollowupStat(models.Model):
|
||||
_name = "followup.stat"
|
||||
_description = "Follow-up Statistics"
|
||||
_rec_name = 'partner_id'
|
||||
_order = 'date_move'
|
||||
_auto = False
|
||||
|
||||
partner_id = fields.Many2one('res.partner', 'Partner', readonly=True)
|
||||
date_move = fields.Date('First move', readonly=True)
|
||||
date_move_last = fields.Date('Last move', readonly=True)
|
||||
date_followup = fields.Date('Latest followup', readonly=True)
|
||||
followup_id = fields.Many2one('followup.line', 'Follow Ups', readonly=True, ondelete="cascade")
|
||||
balance = fields.Float('Balance', readonly=True)
|
||||
debit = fields.Float('Debit', readonly=True)
|
||||
credit = fields.Float('Credit', readonly=True)
|
||||
company_id = fields.Many2one('res.company', 'Company', readonly=True)
|
||||
|
||||
@api.model
|
||||
def init(self):
|
||||
tools.drop_view_if_exists(self.env.cr, 'followup_stat')
|
||||
self.env.cr.execute("""
|
||||
create or replace view followup_stat as (
|
||||
SELECT
|
||||
l.id as id,
|
||||
l.partner_id AS partner_id,
|
||||
min(l.date) AS date_move,
|
||||
max(l.date) AS date_move_last,
|
||||
max(l.followup_date) AS date_followup,
|
||||
max(l.followup_line_id) AS followup_id,
|
||||
sum(l.debit) AS debit,
|
||||
sum(l.credit) AS credit,
|
||||
sum(l.debit - l.credit) AS balance,
|
||||
l.company_id AS company_id
|
||||
FROM
|
||||
account_move_line l
|
||||
LEFT JOIN account_account a ON (l.account_id = a.id)
|
||||
WHERE
|
||||
a.account_type = 'asset_receivable' AND
|
||||
l.full_reconcile_id is NULL AND
|
||||
l.partner_id IS NOT NULL
|
||||
GROUP BY
|
||||
l.id, l.partner_id, l.company_id
|
||||
)""")
|
||||
61
addons/om_account_followup/report/followup_report.xml
Normal file
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="view_om_account_followup_stat_graph" model="ir.ui.view">
|
||||
<field name="name">followup.stat.graph</field>
|
||||
<field name="model">followup.stat</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Follow-up lines">
|
||||
<field name="followup_id" type="row"/>
|
||||
<field name="date_followup" type="col"/>
|
||||
<field name="balance" type="measure"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_om_account_followup_stat_search" model="ir.ui.view">
|
||||
<field name="name">followup.stat.search</field>
|
||||
<field name="model">followup.stat</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Follow-ups Sent">
|
||||
<field name="date_move"/>
|
||||
<field name="date_move_last"/>
|
||||
<separator/>
|
||||
<filter string="Not Litigation" name="not_litigation"
|
||||
help="Including journal entries marked as a litigation"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="balance"/>
|
||||
<group>
|
||||
<filter string="Partner" name="partner"
|
||||
context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Follow-up Level" name="followup_level"
|
||||
context="{'group_by':'followup_id'}"/>
|
||||
<filter string="Company" name="company"
|
||||
groups="base.group_multi_company"
|
||||
context="{'group_by':'company_id'}"/>
|
||||
<separator/>
|
||||
<filter string="Latest Follow-up Month" name="lastest_month"
|
||||
context="{'group_by':'date_followup:month'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_followup_stat" model="ir.actions.act_window">
|
||||
<field name="name">Follow-ups Analysis</field>
|
||||
<field name="res_model">followup.stat</field>
|
||||
<field name="view_mode">graph</field>
|
||||
<field name="context">{'search_default_followup_level':1}</field>
|
||||
<field name="search_view_id" ref="view_om_account_followup_stat_search"/>
|
||||
</record>
|
||||
|
||||
<menuitem action="action_followup_stat"
|
||||
id="menu_action_followup_stat_follow"
|
||||
parent="om_account_followup.menu_finance_followup"
|
||||
groups="account.group_account_invoice"
|
||||
name="Follow-ups Analysis"
|
||||
sequence="20"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
11
addons/om_account_followup/security/ir.model.access.csv
Normal file
@@ -0,0 +1,11 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_followup_followup_line,followup.followup.line,model_followup_line,account.group_account_invoice,1,0,0,0
|
||||
access_followup_followup_line_manager,followup.followup.line.manager,model_followup_line,account.group_account_manager,1,1,1,1
|
||||
access_followup_followup_accountant,followup.followup.user,model_followup_followup,account.group_account_invoice,1,0,0,0
|
||||
access_followup_followup_manager,followup.followup.manager,model_followup_followup,account.group_account_manager,1,1,1,1
|
||||
access_followup_stat_invoice,followup.stat.invoice,model_followup_stat,account.group_account_invoice,1,1,0,0
|
||||
access_followup_stat_by_partner_manager,followup.stat.by.partner,model_followup_stat_by_partner,account.group_account_user,1,1,0,0
|
||||
access_followup_stat_user,followup.stat.user,model_followup_stat,account.group_account_user,1,1,0,0
|
||||
access_followup_stat_manager,followup.stat.manager,model_followup_stat,account.group_account_manager,1,1,1,1
|
||||
access_followup_print,access_followup_print,model_followup_print,base.group_user,1,1,1,1
|
||||
access_followup_sending_results,access_followup_sending_results,model_followup_sending_results,base.group_user,1,1,1,1
|
||||
|
22
addons/om_account_followup/security/security.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="om_account_followup_comp_rule" model="ir.rule">
|
||||
<field name="name">Account Follow-up multi company rule</field>
|
||||
<field name="model_id" ref="model_followup_followup"/>
|
||||
<field eval="True" name="global"/>
|
||||
<field name="domain_force">['|',('company_id','=',False),
|
||||
('company_id','child_of',[user.company_id.id])]</field>
|
||||
</record>
|
||||
|
||||
<record id="om_account_followup_stat_by_partner_comp_rule" model="ir.rule">
|
||||
<field name="name">Account Follow-up Statistics by Partner Rule</field>
|
||||
<field ref="model_followup_stat_by_partner" name="model_id"/>
|
||||
<field eval="True" name="global"/>
|
||||
<field name="domain_force">['|',('company_id','=',False),
|
||||
('company_id','child_of',[user.company_id.id])]</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
BIN
addons/om_account_followup/static/description/banner.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 58 KiB |
BIN
addons/om_account_followup/static/description/followup.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
addons/om_account_followup/static/description/icon.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
81
addons/om_account_followup/static/description/index.html
Normal file
@@ -0,0 +1,81 @@
|
||||
<section class="oe_container oe_dark">
|
||||
<div class="col-md-12">
|
||||
<h2 class="oe_slogan" style="font-size: 35px;color:#2C0091"><b>Customer Follow Up Management</b></h2>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h2 class="oe_slogan" style="color:olive;">Follow-Up Levels</h2>
|
||||
<div class="oe_demo oe_picture oe_screenshot">
|
||||
<img src="follow_up_level.png">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="oe_container oe_dark">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h2 class="oe_slogan" style="color:olive;">Send Follow-Ups</h2>
|
||||
<div class="oe_demo oe_picture oe_screenshot">
|
||||
<img src="followup.png">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<br/>
|
||||
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h2 class="oe_slogan" style="color:olive;">Manual Follow-Ups</h2>
|
||||
<div class="oe_demo oe_picture oe_screenshot">
|
||||
<img src="manual_followup.png">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="oe_container oe_dark">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h2 class="oe_slogan" style="color:olive;">Follow-ups Analysis</h2>
|
||||
<div class="oe_demo oe_picture oe_screenshot">
|
||||
<img src="follow_up_analysis.png">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<hr style="width: 100%;height: 4px;background: #2C0091;margin: 0px 0px;">
|
||||
<hr style="width: 100%;height: 4px;background: #148963;margin: 0px 0px;">
|
||||
<section class="oe_container oe_dark">
|
||||
<div class="oe_row ">
|
||||
<div class="oe_slogan text-center">
|
||||
<img src="odoo_mates.png"/>
|
||||
<div style="color:#269900;">
|
||||
<h3 style="color:#2C0091;font-size: 25px;">If you need any support or want more features, just contact us:</h3><br>
|
||||
<h3 style="color:#2C0091;font-size: 20px;">Email: <a href="odoomates@gmail.com">odoomates@gmail.com</a> <br></h3>
|
||||
</div>
|
||||
<div class="oe_slogan">
|
||||
<h2>
|
||||
<a target="_blank" href="https://www.facebook.com/odoomate/" target="new">
|
||||
<i class="fa fa-facebook-square" style="font-size:38px;"></i>
|
||||
</a>
|
||||
<a target="_blank" href="https://twitter.com/odoomates/" target="new">
|
||||
<i class="fa fa-twitter" style="font-size:38px;"></i>
|
||||
</a>
|
||||
<a href="#" target="_blank">
|
||||
<i class="fa fa-linkedin" style="font-size:38px;"></i>
|
||||
</a>
|
||||
<a target="_blank" href="https://www.youtube.com/channel/UCVKlUZP7HAhdQgs-9iTJklQ">
|
||||
<i class="fa fa-youtube-play" style="font-size:38px;"></i>
|
||||
</a>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<hr style="width: 100%;height: 4px;background: #148963;margin: 0px 0px;">
|
||||
<hr style="width: 100%;height: 4px;background: #2C0091;margin: 0px 0px;">
|
||||
|
||||
|
After Width: | Height: | Size: 43 KiB |
BIN
addons/om_account_followup/static/description/odoo_mates.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
73
addons/om_account_followup/views/account_move.xml
Normal file
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="view_move_line_reconcile_tree" model="ir.ui.view">
|
||||
<field name="name">account.move.line.list</field>
|
||||
<field name="model">account.move.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Journal Items to Reconcile" create="false">
|
||||
<field name="date"/>
|
||||
<field name="move_id"/>
|
||||
<field name="ref"/>
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="account_id"/>
|
||||
<field name="journal_id" invisible="1"/>
|
||||
<field name="full_reconcile_id"/>
|
||||
<field name="debit" sum="Total debit"/>
|
||||
<field name="credit" sum="Total credit"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="account_manual_reconcile_action" model="ir.actions.act_window">
|
||||
<field name="context">{'search_default_unreconciled': 1,'view_mode':True}</field>
|
||||
<field name="name">Journal Items to Reconcile</field>
|
||||
<field name="res_model">account.move.line</field>
|
||||
<field name="view_id" ref="view_move_line_reconcile_tree"/>
|
||||
<field name="view_mode">list</field>
|
||||
<field name="help" type="html">
|
||||
<p>No journal items found.</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="account_move_line_partner_tree" model="ir.ui.view">
|
||||
<field name="name">account.move.line.partner.list</field>
|
||||
<field name="model">account.move.line</field>
|
||||
<field eval="32" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<list editable="bottom" string="Partner Entries">
|
||||
<field name="date"/>
|
||||
<field name="move_id"/>
|
||||
<field name="ref"/>
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="company_id" groups="base.group_multi_company" readonly="1"/>
|
||||
<field name="account_id" options="{'no_open': True, 'no_create': True}"
|
||||
domain="[('company_ids', 'in', company_id)]" groups="account.group_account_readonly"/>
|
||||
<field name="followup_line_id"/>
|
||||
<field name="followup_date"/>
|
||||
<field name="debit" sum="Total debit"/>
|
||||
<field name="credit" sum="Total credit"/>
|
||||
<field name="date_maturity"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_line_form" model="ir.ui.view">
|
||||
<field name="name">account.move.line.form.followup</field>
|
||||
<field name="model">account.move.line</field>
|
||||
<field name="inherit_id" ref="account.view_move_line_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="date_maturity" position="after">
|
||||
<field name="followup_line_id"/>
|
||||
<field name="followup_date"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
38
addons/om_account_followup/views/followup_partner_view.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="om_account_followup_stat_by_partner_search" model="ir.ui.view">
|
||||
<field name="name">followup.stat.by.partner.search</field>
|
||||
<field name="model">followup.stat.by.partner</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Partner to Remind">
|
||||
<field name="date_followup"/>
|
||||
<filter string="Balance > 0"
|
||||
domain="[('balance','>',0)]" icon="terp-dolar"
|
||||
name="balance_positive"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="max_followup_id"/>
|
||||
<field name="company_id"
|
||||
groups="base.group_multi_company"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="om_account_followup_stat_by_partner_tree" model="ir.ui.view">
|
||||
<field name="name">followup.stat.by.partner.list</field>
|
||||
<field name="model">followup.stat.by.partner</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Partner to Remind">
|
||||
<field name="partner_id"/>
|
||||
<field name="balance"/>
|
||||
<field name="max_followup_id"/>
|
||||
<field name="date_followup"/>
|
||||
<field name="date_move_last"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
212
addons/om_account_followup/views/followup_view.xml
Normal file
@@ -0,0 +1,212 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="view_om_account_followup_followup_line_tree" model="ir.ui.view">
|
||||
<field name="name">followup.line.list</field>
|
||||
<field name="model">followup.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Follow-up Steps">
|
||||
<field name="name"/>
|
||||
<field name="delay"/>
|
||||
<field name="send_email"/>
|
||||
<field name="send_letter"/>
|
||||
<field name="manual_action"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_om_account_followup_followup_line_form" model="ir.ui.view">
|
||||
<field name="name">followup.line.form</field>
|
||||
<field name="model">followup.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Follow-up Steps">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
<field name="name"/>
|
||||
</h1>
|
||||
<div class="oe_inline">
|
||||
After
|
||||
<field name="delay" class="oe_inline"/>
|
||||
days overdue, do the following actions:
|
||||
</div>
|
||||
<div>
|
||||
<field name="manual_action" class="oe_inline"/>
|
||||
<label for="manual_action"/>
|
||||
</div>
|
||||
<div>
|
||||
<field name="send_email" class="oe_inline"/>
|
||||
<label for="send_email"/>
|
||||
</div>
|
||||
<div>
|
||||
<field name="send_letter" class="oe_inline"/>
|
||||
<label for="send_letter"/>
|
||||
</div>
|
||||
|
||||
<group string="Manual Action" invisible="not manual_action">
|
||||
<field name="manual_action_responsible_id"/>
|
||||
<field name="manual_action_note"/>
|
||||
</group>
|
||||
<group string="Send an Email" invisible="not send_email">
|
||||
<field name="email_template_id"/>
|
||||
</group>
|
||||
<group string="Send a Letter or Email"
|
||||
invisible="not send_email and not send_letter">
|
||||
<p colspan="2" class="oe_grey">
|
||||
Write here the introduction in the letter,
|
||||
according to the level of the follow-up. You can
|
||||
use the following keywords in the text. Don't
|
||||
forget to translate in all languages you installed
|
||||
using to top right icon.
|
||||
<table>
|
||||
<tr>
|
||||
<td t-translation="off">%%(partner_name)s
|
||||
</td>
|
||||
<td>: Partner Name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td t-translation="off">%%(date)s</td>
|
||||
<td>: Current Date</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td t-translation="off">
|
||||
%%(user_signature)s
|
||||
</td>
|
||||
<td>: User Name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td t-translation="off">%%(company_name)s
|
||||
</td>
|
||||
<td>: User's Company Name</td>
|
||||
</tr>
|
||||
</table>
|
||||
</p>
|
||||
<field name="description" nolabel="1" colspan="2"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_om_account_followup_followup_form" model="ir.ui.view">
|
||||
<field name="name">followup.followup.form</field>
|
||||
<field name="model">followup.followup</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Follow-up">
|
||||
<h1>
|
||||
<field name="name"/>
|
||||
</h1>
|
||||
<label for="company_id" groups="base.group_multi_company"/>
|
||||
<field name="company_id" widget="selection"
|
||||
class="oe_inline"
|
||||
groups="base.group_multi_company"/>
|
||||
<p class="oe_grey">
|
||||
To remind customers of paying their invoices, you can
|
||||
define different actions depending on how severely
|
||||
overdue the customer is. These actions are bundled
|
||||
into follow-up levels that are triggered when the due
|
||||
date of an invoice has passed a certain
|
||||
number of days. If there are other overdue invoices for
|
||||
the
|
||||
same customer, the actions of the most
|
||||
overdue invoice will be executed.
|
||||
</p>
|
||||
<field name="followup_line"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_om_account_followup_followup_tree" model="ir.ui.view">
|
||||
<field name="name">followup.followup.list</field>
|
||||
<field name="model">followup.followup</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Follow-up">
|
||||
<field name="company_id"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_om_account_followup_filter" model="ir.ui.view">
|
||||
<field name="name">account.followup.select</field>
|
||||
<field name="model">followup.followup</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Follow-up">
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_om_account_followup_definition_form" model="ir.actions.act_window">
|
||||
<field name="name">Follow-up Levels</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">followup.followup</field>
|
||||
<field name="search_view_id" ref="view_om_account_followup_filter"/>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to define follow-up levels and their related actions.
|
||||
</p>
|
||||
<p>
|
||||
For each step, specify the actions to be taken and delay in
|
||||
days. It is
|
||||
possible to use print and e-mail templates to send specific
|
||||
messages to
|
||||
the customer.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_line_reconcile_tree" model="ir.ui.view">
|
||||
<field name="name">account.move.line.list</field>
|
||||
<field name="model">account.move.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Journal Items to Reconcile" create="false">
|
||||
<field name="date"/>
|
||||
<field name="move_id"/>
|
||||
<field name="ref"/>
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="account_id"/>
|
||||
<field name="journal_id" invisible="1"/>
|
||||
<field name="full_reconcile_id"/>
|
||||
<field name="debit" sum="Total debit"/>
|
||||
<field name="credit" sum="Total credit"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="account_manual_reconcile_action" model="ir.actions.act_window">
|
||||
<field name="context">{'search_default_unreconciled': 1,'view_mode':True}</field>
|
||||
<field name="name">Journal Items to Reconcile</field>
|
||||
<field name="res_model">account.move.line</field>
|
||||
<field name="view_id" ref="view_move_line_reconcile_tree"/>
|
||||
<field name="view_mode">list</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
No journal items found.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="om_account_followup_main_menu"
|
||||
parent="account.menu_finance_configuration"
|
||||
name="Follow-up"/>
|
||||
|
||||
<menuitem id="om_account_followup_menu"
|
||||
name="Follow-up Levels"
|
||||
action="action_om_account_followup_definition_form"
|
||||
parent="om_account_followup_main_menu" />
|
||||
|
||||
<menuitem id="om_account_followup_main_menu"
|
||||
parent="account.menu_finance_configuration"
|
||||
name="Follow-up"/>
|
||||
|
||||
<menuitem id="om_account_followup_menu"
|
||||
name="Follow-up Levels"
|
||||
action="action_om_account_followup_definition_form"
|
||||
parent="om_account_followup_main_menu"/>
|
||||
|
||||
|
||||
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
192
addons/om_account_followup/views/partners.xml
Normal file
@@ -0,0 +1,192 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="customer_followup_tree" model="ir.ui.view">
|
||||
<field name="name">res.partner.followup.inherit.list</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Customer Followup" create="false" delete="false">
|
||||
<field name="display_name"/>
|
||||
<field name="payment_next_action_date"/>
|
||||
<field name="payment_next_action"/>
|
||||
<field name="user_id" invisible="1"/>
|
||||
<field name="country_id" invisible="1"/>
|
||||
<field name="parent_id" invisible="1"/>
|
||||
<field name="payment_responsible_id"/>
|
||||
<field name="payment_earliest_due_date"/>
|
||||
<field name="latest_followup_level_id"/>
|
||||
<field name="payment_amount_overdue"/>
|
||||
<field name="payment_amount_due"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_partner_inherit_customer_followup_tree" model="ir.ui.view">
|
||||
<field name="name">res.partner.followup.inherit.list</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="complete_name" position="after">
|
||||
<field name="payment_responsible_id" invisible="1"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="customer_followup_search_view" model="ir.ui.view">
|
||||
<field name="name">Search</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_res_partner_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//group[1]" position="after">
|
||||
<group>
|
||||
<filter string="Partners with Overdue Credits" domain="[('payment_amount_overdue', '>', 0.0)]"
|
||||
name="credits"/>
|
||||
<separator/>
|
||||
<filter string="Follow-ups To Do"
|
||||
domain="[('payment_next_action_date', '<=', time.strftime('%%Y-%%m-%%d')), ('payment_amount_overdue', '>', 0.0)]"
|
||||
name="todo"/>
|
||||
<separator/>
|
||||
<filter string="No Responsible" name="no_responsibe" domain="[('payment_responsible_id', '=', False)]"/>
|
||||
<filter string="My Follow-ups" domain="[('payment_responsible_id','=', uid)]" name="my"/>
|
||||
</group>
|
||||
</xpath>
|
||||
<xpath expr="//group[1]" position="inside">
|
||||
<filter string="Follow-up Responsible" name="responsibe"
|
||||
context="{'group_by':'payment_responsible_id'}"/>
|
||||
<filter string="Followup Level" name="followup_level"
|
||||
context="{'group_by':'latest_followup_level_id'}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_customer_followup" model="ir.actions.act_window">
|
||||
<field name="name">Manual Follow-Ups</field>
|
||||
<field name="view_id" ref="customer_followup_tree"/>
|
||||
<field name="res_model">res.partner</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="domain">[('payment_amount_due', '>', 0.0)]</field>
|
||||
<field name="context">{'Followupfirst':True, 'search_default_todo': True}</field>
|
||||
<field name="search_view_id" ref="customer_followup_search_view"/>
|
||||
</record>
|
||||
|
||||
<record id="view_partner_inherit_followup_form" model="ir.ui.view">
|
||||
<field name="name">res.partner.followup.form.inherit</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='sales_purchases']" position="after">
|
||||
<page string="Payment Follow-up"
|
||||
groups="account.group_account_invoice"
|
||||
name="followup_tab">
|
||||
|
||||
<div class="oe_right"
|
||||
name="followup_button">
|
||||
<button name="do_button_print" type="object"
|
||||
string="Print Overdue Payments"
|
||||
groups="account.group_account_user"
|
||||
help="Print overdue payments report independent of follow-up line"
|
||||
invisible="payment_amount_due <= 0.0"/>
|
||||
<button name="do_partner_mail" type="object"
|
||||
string="Send Overdue Email"
|
||||
groups="account.group_account_user"
|
||||
help="If not specified by the latest follow-up level, it will send from the default email template"
|
||||
invisible="payment_amount_due <= 0.0"/>
|
||||
</div>
|
||||
|
||||
<p invisible="not latest_followup_date">
|
||||
The
|
||||
<field name="latest_followup_date"
|
||||
class="oe_inline"/>
|
||||
, the latest payment follow-up was:
|
||||
<field name="latest_followup_level_id"
|
||||
class="oe_inline"/>
|
||||
</p>
|
||||
<group>
|
||||
<field name="payment_responsible_id"
|
||||
placeholder="Responsible of credit collection"
|
||||
class="oe_inline"/>
|
||||
<label for="payment_next_action"/>
|
||||
<div>
|
||||
<field name="payment_next_action_date"
|
||||
class="oe_inline"/>
|
||||
<button name="action_done" type="object"
|
||||
string="⇾ Mark as Done"
|
||||
help="Click to mark the action as done."
|
||||
class="oe_link"
|
||||
invisible="not payment_next_action_date"
|
||||
groups="account.group_account_user"/>
|
||||
<field name="payment_next_action"
|
||||
placeholder="Action to be taken e.g. Give a phonecall, Check if it's paid, ..."/>
|
||||
</div>
|
||||
</group>
|
||||
<label for="payment_note" class="oe_edit_only"/>
|
||||
<field name="payment_note"
|
||||
placeholder="He said the problem was temporary and promised to pay 50%% before 15th of May, balance before 1st of July."/>
|
||||
<p class="oe_grey">
|
||||
Below is the history of the transactions of this
|
||||
customer. You can check "No Follow-up" in
|
||||
order to exclude it from the next follow-up
|
||||
actions.
|
||||
</p>
|
||||
<field name="unreconciled_aml_ids">
|
||||
<list string="Account Move line" editable="bottom"
|
||||
create="false" delete="false"
|
||||
colors="red:(not date_maturity or date_maturity<=current_date) and result>0">
|
||||
<field name="date" readonly="True"/>
|
||||
<field name="company_id" readonly="True"
|
||||
groups="base.group_multi_company"/>
|
||||
<field name="move_id" readonly="True"/>
|
||||
<field name="date_maturity" readonly="True"/>
|
||||
<field name="result" readonly="True"/>
|
||||
<field name="followup_line_id" invisible='1'/>
|
||||
</list>
|
||||
</field>
|
||||
<group class="oe_subtotal_footer oe_right">
|
||||
<field name="payment_amount_due"/>
|
||||
</group>
|
||||
<div class="oe_clear"/>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_view_customer_followup_form" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="2"/>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_partner_inherit_followup_form"/>
|
||||
<field name="act_window_id" ref="action_customer_followup"/>
|
||||
</record>
|
||||
|
||||
<record id="action_view_customer_followup_tree" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="1"/>
|
||||
<field name="view_mode">list</field>
|
||||
<field name="view_id" ref="customer_followup_tree"/>
|
||||
<field name="act_window_id" ref="action_customer_followup"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="om_account_followup_s"
|
||||
action="action_customer_followup"
|
||||
parent="menu_finance_followup"
|
||||
name="Do Manual Follow-Ups"
|
||||
sequence="3"/>
|
||||
|
||||
<record id="action_customer_my_followup" model="ir.actions.act_window">
|
||||
<field name="name">My Follow-Ups</field>
|
||||
<field name="view_id" ref="customer_followup_tree"/>
|
||||
<field name="res_model">res.partner</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="domain">[('payment_amount_due', '>', 0.0)]</field>
|
||||
<field name="context">{'Followupfirst':True, 'search_default_todo': True, 'search_default_my': True}</field>
|
||||
<field name="search_view_id" ref="customer_followup_search_view"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_sale_followup"
|
||||
parent="menu_finance_followup"
|
||||
sequence="10"
|
||||
action="action_customer_my_followup"
|
||||
groups="account.group_account_invoice"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
75
addons/om_account_followup/views/report_followup.xml
Normal file
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<template id="report_followup">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="ids_to_objects(data['partner_ids'])" t-as="o">
|
||||
<t t-set="o" t-value="o.with_context({'lang':o.partner_id.lang})"/>
|
||||
<t t-call="web.external_layout">
|
||||
<div class="page">
|
||||
<p>
|
||||
<span t-field="o.invoice_partner_id"/>
|
||||
<br/>
|
||||
<t t-if="o.partner_id.vat">
|
||||
<span t-field="o.partner_id.vat"/>
|
||||
<br/>
|
||||
</t>
|
||||
Document: Customer account statement
|
||||
<br/>
|
||||
Date:
|
||||
<span t-esc="data['date']"/>
|
||||
<br/>
|
||||
Customer ref:
|
||||
<span t-field="o.partner_id.ref"/>
|
||||
</p>
|
||||
|
||||
<p t-raw="get_text(o,data['followup_id']).replace('\n', '<br>')"/>
|
||||
|
||||
<t t-foreach="getLines(o)" t-as="cur_lines">
|
||||
<table class="table table-condensed"
|
||||
style="margin-top: 50px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Invoice Date</th>
|
||||
<th>Description</th>
|
||||
<th class="text-center">Ref</th>
|
||||
<th class="text-center">Maturity Date</th>
|
||||
<th class="text-right">Amount</th>
|
||||
<th class="text-center">Due</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="cur_lines['line']"
|
||||
t-as="line">
|
||||
<td>
|
||||
<span t-esc="line['date']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="line['name']"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="line['ref']"/>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span t-esc="line['date_maturity']"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="line['balance']"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p>Total:
|
||||
<span t-esc="cur_lines['total']"/>
|
||||
</p>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
14
addons/om_account_followup/views/reports.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="action_report_followup" model="ir.actions.report">
|
||||
<field name="name">Follow-up Report</field>
|
||||
<field name="model">followup.followup</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">om_account_followup.report_followup</field>
|
||||
<field name="report_file">om_account_followup.report_followup</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
3
addons/om_account_followup/wizard/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from . import followup_print
|
||||
from . import followup_results
|
||||
|
||||
224
addons/om_account_followup/wizard/followup_print.py
Normal file
@@ -0,0 +1,224 @@
|
||||
import datetime
|
||||
import time
|
||||
from odoo import api, fields, models, _
|
||||
from markupsafe import Markup
|
||||
|
||||
|
||||
class FollowupPrint(models.TransientModel):
|
||||
_name = 'followup.print'
|
||||
_description = 'Print Follow-up & Send Mail to Customers'
|
||||
|
||||
def _get_followup(self):
|
||||
if self.env.context.get('active_model',
|
||||
'ir.ui.menu') == 'followup.followup':
|
||||
return self.env.context.get('active_id', False)
|
||||
company_id = self.env.user.company_id.id
|
||||
followp_id = self.env['followup.followup'].search(
|
||||
[('company_id', '=', company_id)], limit=1)
|
||||
return followp_id or False
|
||||
|
||||
date = fields.Date('Follow-up Sending Date', required=True,
|
||||
help="This field allow you to select a forecast date "
|
||||
"to plan your follow-ups",
|
||||
default=lambda *a: time.strftime('%Y-%m-%d'))
|
||||
followup_id = fields.Many2one('followup.followup', 'Follow-Up',
|
||||
required=True, readonly=True,
|
||||
default=_get_followup)
|
||||
partner_ids = fields.Many2many('followup.stat.by.partner',
|
||||
'partner_stat_rel', 'osv_memory_id',
|
||||
'partner_id', 'Partners', required=True)
|
||||
company_id = fields.Many2one('res.company', readonly=True,
|
||||
related='followup_id.company_id')
|
||||
email_conf = fields.Boolean('Send Email Confirmation')
|
||||
email_subject = fields.Char('Email Subject', size=64,
|
||||
default=_('Invoices Reminder'))
|
||||
partner_lang = fields.Boolean(
|
||||
'Send Email in Partner Language', default=True,
|
||||
help='Do not change message text, if you want to send email in '
|
||||
'partner language, or configure from company')
|
||||
email_body = fields.Text('Email Body', default='')
|
||||
summary = fields.Text('Summary', readonly=True)
|
||||
test_print = fields.Boolean(
|
||||
'Test Print', help='Check if you want to print follow-ups without '
|
||||
'changing follow-up level.')
|
||||
|
||||
def process_partners(self, partner_ids, data):
|
||||
partner_obj = self.env['res.partner']
|
||||
partner_ids_to_print = []
|
||||
nbmanuals = 0
|
||||
manuals = {}
|
||||
nbmails = 0
|
||||
nbunknownmails = 0
|
||||
nbprints = 0
|
||||
resulttext = " "
|
||||
for partner in self.env['followup.stat.by.partner'].browse(
|
||||
partner_ids):
|
||||
if partner.max_followup_id.manual_action:
|
||||
partner_obj.do_partner_manual_action([partner.partner_id.id])
|
||||
nbmanuals = nbmanuals + 1
|
||||
key = partner.partner_id.payment_responsible_id.name or _(
|
||||
"Anybody")
|
||||
if key not in manuals.keys():
|
||||
manuals[key] = 1
|
||||
else:
|
||||
manuals[key] = manuals[key] + 1
|
||||
if partner.max_followup_id.send_email:
|
||||
nbunknownmails += partner.partner_id.do_partner_mail()
|
||||
nbmails += 1
|
||||
if partner.max_followup_id.send_letter:
|
||||
partner_ids_to_print.append(partner.id)
|
||||
nbprints += 1
|
||||
followup_without_lit = \
|
||||
partner.partner_id.latest_followup_level_id_without_lit
|
||||
message = "%s<I> %s </I>%s" % (_("Follow-up letter of "),
|
||||
followup_without_lit.name,
|
||||
_(" will be sent"))
|
||||
partner.partner_id.message_post(body=message)
|
||||
if nbunknownmails == 0:
|
||||
resulttext += str(nbmails) + _(" email(s) sent")
|
||||
else:
|
||||
resulttext += str(nbmails) + _(
|
||||
" email(s) should have been sent, but ") + str(
|
||||
nbunknownmails) + _(
|
||||
" had unknown email address(es)") + "\n <BR/> "
|
||||
resulttext += "<BR/>" + str(nbprints) + _(
|
||||
" letter(s) in report") + " \n <BR/>" + str(nbmanuals) + _(
|
||||
" manual action(s) assigned:")
|
||||
needprinting = False
|
||||
if nbprints > 0:
|
||||
needprinting = True
|
||||
resulttext += "<p align=\"center\">"
|
||||
for item in manuals:
|
||||
resulttext = resulttext + "<li>" + item + ":" + str(
|
||||
manuals[item]) + "\n </li>"
|
||||
resulttext += "</p>"
|
||||
result = {}
|
||||
action = partner_obj.do_partner_print(partner_ids_to_print, data)
|
||||
result['needprinting'] = needprinting
|
||||
result['resulttext'] = Markup(resulttext)
|
||||
result['action'] = action or {}
|
||||
return result
|
||||
|
||||
def do_update_followup_level(self, to_update, partner_list, date):
|
||||
for id in to_update.keys():
|
||||
if to_update[id]['partner_id'] in partner_list:
|
||||
self.env['account.move.line'].browse([int(id)]).write(
|
||||
{'followup_line_id': to_update[id]['level'],
|
||||
'followup_date': date})
|
||||
|
||||
def clear_manual_actions(self, partner_list):
|
||||
partner_list_ids = [partner.partner_id.id for partner in self.env[
|
||||
'followup.stat.by.partner'].browse(partner_list)]
|
||||
ids = self.env['res.partner'].search(
|
||||
['&', ('id', 'not in', partner_list_ids), '|',
|
||||
('payment_responsible_id', '!=', False),
|
||||
('payment_next_action_date', '!=', False)])
|
||||
|
||||
partners_to_clear = []
|
||||
for part in ids:
|
||||
if not part.unreconciled_aml_ids:
|
||||
partners_to_clear.append(part.id)
|
||||
part.action_done()
|
||||
return len(partners_to_clear)
|
||||
|
||||
def do_process(self):
|
||||
context = dict(self.env.context or {})
|
||||
|
||||
tmp = self._get_partners_followp()
|
||||
partner_list = tmp['partner_ids']
|
||||
to_update = tmp['to_update']
|
||||
date = self.date
|
||||
data = self.read()[0]
|
||||
data['followup_id'] = data['followup_id'][0]
|
||||
|
||||
self.do_update_followup_level(to_update, partner_list, date)
|
||||
restot_context = context.copy()
|
||||
restot = self.with_context(restot_context).process_partners(
|
||||
partner_list, data)
|
||||
context.update(restot_context)
|
||||
nbactionscleared = self.clear_manual_actions(partner_list)
|
||||
if nbactionscleared > 0:
|
||||
restot['resulttext'] = restot['resulttext'] + "<li>" + _(
|
||||
"%s partners have no credits and as such the "
|
||||
"action is cleared") % (str(nbactionscleared)) + "</li>"
|
||||
resource_id = self.env.ref(
|
||||
'om_account_followup.view_om_account_followup_sending_results')
|
||||
context.update({'description': restot['resulttext'],
|
||||
'needprinting': restot['needprinting'],
|
||||
'report_data': restot['action']})
|
||||
return {
|
||||
'name': _('Send Letters and Emails: Actions Summary'),
|
||||
'view_type': 'form',
|
||||
'context': context,
|
||||
'view_mode': 'list,form',
|
||||
'res_model': 'followup.sending.results',
|
||||
'views': [(resource_id.id, 'form')],
|
||||
'type': 'ir.actions.act_window',
|
||||
'target': 'new',
|
||||
}
|
||||
|
||||
def _get_msg(self):
|
||||
return self.env.user.company_id.follow_up_msg
|
||||
|
||||
def _get_partners_followp(self):
|
||||
data = self
|
||||
company_id = data.company_id.id
|
||||
context = self.env.context
|
||||
self.env.cr.execute(
|
||||
'''SELECT
|
||||
l.partner_id,
|
||||
l.followup_line_id,
|
||||
l.date_maturity,
|
||||
l.date, l.id
|
||||
FROM account_move_line AS l
|
||||
LEFT JOIN account_account AS a
|
||||
ON (l.account_id=a.id)
|
||||
WHERE (l.full_reconcile_id IS NULL)
|
||||
AND a.account_type = 'asset_receivable'
|
||||
AND (l.partner_id is NOT NULL)
|
||||
AND (l.debit > 0)
|
||||
AND (l.company_id = %s)
|
||||
ORDER BY l.date''' % (company_id))
|
||||
move_lines = self.env.cr.fetchall()
|
||||
old = None
|
||||
fups = {}
|
||||
fup_id = 'followup_id' in context and context[
|
||||
'followup_id'] or data.followup_id.id
|
||||
date = 'date' in context and context['date'] or data.date
|
||||
date = fields.Date.to_string(date)
|
||||
current_date = datetime.date(*time.strptime(date, '%Y-%m-%d')[:3])
|
||||
self.env.cr.execute(
|
||||
'''SELECT *
|
||||
FROM followup_line
|
||||
WHERE followup_id=%s
|
||||
ORDER BY delay''' % (fup_id,))
|
||||
|
||||
for result in self.env.cr.dictfetchall():
|
||||
delay = datetime.timedelta(days=result['delay'])
|
||||
fups[old] = (current_date - delay, result['id'])
|
||||
old = result['id']
|
||||
|
||||
partner_list = []
|
||||
to_update = {}
|
||||
|
||||
for partner_id, followup_line_id, date_maturity, date, id in \
|
||||
move_lines:
|
||||
if not partner_id:
|
||||
continue
|
||||
if followup_line_id not in fups:
|
||||
continue
|
||||
stat_line_id = partner_id * 10000 + company_id
|
||||
if date_maturity:
|
||||
date_maturity = fields.Date.to_string(date_maturity)
|
||||
if date_maturity <= fups[followup_line_id][0].strftime(
|
||||
'%Y-%m-%d'):
|
||||
if stat_line_id not in partner_list:
|
||||
partner_list.append(stat_line_id)
|
||||
to_update[str(id)] = {'level': fups[followup_line_id][1],
|
||||
'partner_id': stat_line_id}
|
||||
elif date and date <= fups[followup_line_id][0]:
|
||||
if stat_line_id not in partner_list:
|
||||
partner_list.append(stat_line_id)
|
||||
to_update[str(id)] = {'level': fups[followup_line_id][1],
|
||||
'partner_id': stat_line_id}
|
||||
return {'partner_ids': partner_list, 'to_update': to_update}
|
||||
52
addons/om_account_followup/wizard/followup_print_view.xml
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="view_om_account_followup_print" model="ir.ui.view">
|
||||
<field name="name">account.followup.print.form</field>
|
||||
<field name="model">followup.print</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Send follow-ups">
|
||||
<group col="4">
|
||||
<field name="date" groups="base.group_no_one"/>
|
||||
<field name="followup_id"
|
||||
groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<p class="oe_grey">
|
||||
This action will send follow-up emails, print the
|
||||
letters and
|
||||
set the manual actions per customer, according to the
|
||||
follow-up levels defined.
|
||||
</p>
|
||||
<footer>
|
||||
<button name="do_process"
|
||||
string="Send emails and generate letters"
|
||||
type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="Cancel" class="oe_link"
|
||||
special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_om_account_followup_print" model="ir.actions.act_window">
|
||||
<field name="name">Send Follow-Ups</field>
|
||||
<field name="res_model">followup.print</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_finance_followup" parent="account.menu_finance"
|
||||
name="Follow-Ups"
|
||||
groups="account.group_account_invoice"/>
|
||||
|
||||
<menuitem action="action_om_account_followup_print"
|
||||
id="om_account_followup_print_menu"
|
||||
parent="menu_finance_followup"
|
||||
name="Send Letters and Emails"
|
||||
groups="account.group_account_user,account.group_account_manager"
|
||||
sequence="2"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
21
addons/om_account_followup/wizard/followup_results.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
class FollowupSendingResults(models.TransientModel):
|
||||
_name = 'followup.sending.results'
|
||||
_description = 'Results from the sending of the different letters and emails'
|
||||
|
||||
def do_report(self):
|
||||
return self.env.context.get('report_data')
|
||||
|
||||
def do_done(self):
|
||||
return {}
|
||||
|
||||
def _get_description(self):
|
||||
return self.env.context.get('description')
|
||||
|
||||
def _get_need_printing(self):
|
||||
return self.env.context.get('needprinting')
|
||||
|
||||
description = fields.Html("Description", readonly=True, default=_get_description)
|
||||
needprinting = fields.Boolean("Needs Printing", default=_get_need_printing)
|
||||
27
addons/om_account_followup/wizard/followup_results_view.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="view_om_account_followup_sending_results" model="ir.ui.view">
|
||||
<field name="name">followup.sending.results.form</field>
|
||||
<field name="model">followup.sending.results</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Summary of actions">
|
||||
<field name="description" class="oe_view_only"/>
|
||||
<footer>
|
||||
<field name="needprinting" invisible="1"/>
|
||||
<div invisible="not needprinting">
|
||||
<button name="do_report" string="Download Letters"
|
||||
type="object" class="oe_highlight"/>
|
||||
</div>
|
||||
<div invisible="needprinting">
|
||||
<button name="do_done" string="Close" type="object"
|
||||
class="oe_highlight"/>
|
||||
</div>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
2
addons/om_fiscal_year/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from . import wizard
|
||||
from . import models
|
||||
23
addons/om_fiscal_year/__manifest__.py
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
'name': 'Odoo 19 Fiscal Year & Lock Date',
|
||||
'version': '19.0.1.0.1', # __odoosky_original_version__: '1.0.1'
|
||||
'category': 'Accounting',
|
||||
'summary': 'Odoo 19 Fiscal Year, Fiscal Year in Odoo 19, Lock Date in Odoo 19',
|
||||
'description': 'Odoo 19 Fiscal Year, Fiscal Year in Odoo 19',
|
||||
'live_test_url': 'https://www.youtube.com/watch?v=Kj4hR7_uNs4',
|
||||
'sequence': '1',
|
||||
'website': 'https://www.odoomates.tech',
|
||||
'author': 'Odoo Mates, Odoo SA',
|
||||
'maintainer': 'Odoo Mates',
|
||||
'license': 'LGPL-3',
|
||||
'support': 'odoomates@gmail.com',
|
||||
'depends': ['account'],
|
||||
'data': [
|
||||
'security/security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'wizard/change_lock_date.xml',
|
||||
'views/fiscal_year.xml',
|
||||
'views/settings.xml',
|
||||
],
|
||||
'images': ['static/description/banner.png'],
|
||||
}
|
||||
291
addons/om_fiscal_year/i18n/ar_001.po
Normal file
@@ -0,0 +1,291 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * om_fiscal_year
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 15.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-04-15 18:18+0000\n"
|
||||
"PO-Revision-Date: 2022-04-15 18:18+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Account Period Closing"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:res.groups,name:om_fiscal_year.group_fiscal_year
|
||||
msgid "Allow to define fiscal years of more or less than a year"
|
||||
msgstr "تسمح بتحديد السنوات المالية التي تزيد أو تقل عن عام\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_change_lock_date
|
||||
msgid "Change Lock Date"
|
||||
msgstr "تغيير تاريخ القفل\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.actions.act_window,help:om_fiscal_year.actions_account_fiscal_year
|
||||
msgid "Click here to create a new fiscal year."
|
||||
msgstr "انقر هنا لإنشاء سنة مالية جديدة.\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "شركات"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__company_id
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__company_id
|
||||
msgid "Company"
|
||||
msgstr "الشركة"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_res_config_settings
|
||||
msgid "Config Settings"
|
||||
msgstr "ضبط الاعدادات"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__create_uid
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__create_uid
|
||||
msgid "Created by"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__create_date
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__create_date
|
||||
msgid "Created on"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Define fiscal years of more or less than one year"
|
||||
msgstr "تحديد السنوات المالية التي تزيد أو تقل عن سنة واحدة\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__display_name
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__display_name
|
||||
msgid "Display Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__date_to
|
||||
msgid "End Date"
|
||||
msgstr "تاريخ الانتهاء"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_account_fiscal_year__date_to
|
||||
msgid "Ending Date, included in the fiscal year."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Period Closing"
|
||||
msgstr "إغلاق الفترة المالية\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_account_fiscal_year
|
||||
#: model:ir.ui.menu,name:om_fiscal_year.menu_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Year"
|
||||
msgstr "السنة المالية\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.actions.act_window,name:om_fiscal_year.actions_account_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__group_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Years"
|
||||
msgstr "السنوات المالية"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_last_day
|
||||
msgid "Fiscalyear Last Day"
|
||||
msgstr "آخر أيام السنة المالية"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_last_month
|
||||
msgid "Fiscalyear Last Month"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__id
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__id
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__period_lock_date
|
||||
msgid "Journal Entries Lock Date"
|
||||
msgstr "تاريخ قفل إدخالات دفتر اليومية\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Last Day"
|
||||
msgstr "بالأمس"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year____last_update
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date____last_update
|
||||
msgid "Last Modified on"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__write_uid
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__write_date
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_lock_date
|
||||
msgid "Lock Date"
|
||||
msgstr "تاريخ القفل\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__fiscalyear_lock_date
|
||||
msgid "Lock Date for All Users"
|
||||
msgstr "تاريخ القفل لجميع المستخدمين\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__period_lock_date
|
||||
msgid "Lock Date for Non-Advisers"
|
||||
msgstr "تاريخ القفل لغير المستشارين\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.ui.menu,name:om_fiscal_year.menu_action_change_lock_date
|
||||
msgid "Lock Dates"
|
||||
msgstr "تواريخ القفل\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.actions.act_window,name:om_fiscal_year.action_view_change_lock_date
|
||||
msgid "Lock your Fiscal Period"
|
||||
msgstr "قفل الفترة المالية الخاصة بك\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Lock your fiscal period"
|
||||
msgstr "قفل الفترة المالية الخاصة بك\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Management Closing"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__name
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__tax_lock_date
|
||||
msgid ""
|
||||
"No users can edit journal entries related to a tax prior and inclusive of "
|
||||
"this date."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_res_config_settings__fiscalyear_lock_date
|
||||
msgid ""
|
||||
"No users, including Advisers, can edit accounts prior to and inclusive of "
|
||||
"this date. Use it for fiscal year locking for example."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__fiscalyear_lock_date
|
||||
msgid ""
|
||||
"No users, including Advisers, can edit accounts prior to and inclusive of "
|
||||
"this date. Use it for fiscal year locking."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_res_config_settings__period_lock_date
|
||||
msgid ""
|
||||
"Only users with the 'Adviser' role can edit accounts prior to and inclusive "
|
||||
"of this date. Use it for period locking inside an open fiscal year, for "
|
||||
"example."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__period_lock_date
|
||||
msgid "Prevent posting of journal entries in this period."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "Show unposted entries"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__date_from
|
||||
msgid "Start Date"
|
||||
msgstr "تاريخ البداية"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_account_fiscal_year__date_from
|
||||
msgid "Start Date, included in the fiscal year."
|
||||
msgstr "تاريخ البدء مضمن في السنة المالية.\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__tax_lock_date
|
||||
msgid "Tax Lock Date"
|
||||
msgstr "تاريخ القفل الضريبي\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/account_fiscal_year.py:0
|
||||
#, python-format
|
||||
msgid "The ending date must not be prior to the starting date."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"There are still unposted entries in the period you want to lock. You should "
|
||||
"either post or delete them."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"There are still unreconciled bank statement lines in the period you want to "
|
||||
"lock.You should either reconcile or delete them."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/wizard/change_lock_date.py:0
|
||||
#, python-format
|
||||
msgid "You Are Not Allowed To Perform This Operation"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/account_fiscal_year.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can not have an overlap between two fiscal years, please correct the "
|
||||
"start and/or end dates of your fiscal years."
|
||||
msgstr ""
|
||||
"لا يمكن أن يكون هناك تداخل بين سنتين ماليتين ، يرجى تصحيح تواريخ البدء و / "
|
||||
"أو الانتهاء للسنتين الماليتين.\n"
|
||||
289
addons/om_fiscal_year/i18n/ar_SY.po
Normal file
@@ -0,0 +1,289 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * om_fiscal_year
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 15.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-07-06 02:59+0000\n"
|
||||
"PO-Revision-Date: 2022-07-06 02:59+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Account Period Closing"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:res.groups,name:om_fiscal_year.group_fiscal_year
|
||||
msgid "Allow to define fiscal years of more or less than a year"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_change_lock_date
|
||||
msgid "Change Lock Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.actions.act_window,help:om_fiscal_year.actions_account_fiscal_year
|
||||
msgid "Click here to create a new fiscal year."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "الشركات "
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__company_id
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__company_id
|
||||
msgid "Company"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_res_config_settings
|
||||
msgid "Config Settings"
|
||||
msgstr "ضبط الاعدادات"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__create_uid
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__create_uid
|
||||
msgid "Created by"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__create_date
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__create_date
|
||||
msgid "Created on"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Define fiscal years of more or less than one year"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__display_name
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__display_name
|
||||
msgid "Display Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__date_to
|
||||
msgid "End Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_account_fiscal_year__date_to
|
||||
msgid "Ending Date, included in the fiscal year."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Period Closing"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_account_fiscal_year
|
||||
#: model:ir.ui.menu,name:om_fiscal_year.menu_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Year"
|
||||
msgstr "السنة المالية\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.actions.act_window,name:om_fiscal_year.actions_account_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__group_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Years"
|
||||
msgstr "السنوات المالية\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_last_day
|
||||
msgid "Fiscalyear Last Day"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_last_month
|
||||
msgid "Fiscalyear Last Month"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__id
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__id
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__period_lock_date
|
||||
msgid "Journal Entries Lock Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Last Day"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year____last_update
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date____last_update
|
||||
msgid "Last Modified on"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__write_uid
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__write_date
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_lock_date
|
||||
msgid "Lock Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__fiscalyear_lock_date
|
||||
msgid "Lock Date for All Users"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__period_lock_date
|
||||
msgid "Lock Date for Non-Advisers"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.ui.menu,name:om_fiscal_year.menu_action_change_lock_date
|
||||
msgid "Lock Dates"
|
||||
msgstr "تواريخ القفل\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.actions.act_window,name:om_fiscal_year.action_view_change_lock_date
|
||||
msgid "Lock your Fiscal Period"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Lock your fiscal period"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Management Closing"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__name
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__tax_lock_date
|
||||
msgid ""
|
||||
"No users can edit journal entries related to a tax prior and inclusive of "
|
||||
"this date."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_res_config_settings__fiscalyear_lock_date
|
||||
msgid ""
|
||||
"No users, including Advisers, can edit accounts prior to and inclusive of "
|
||||
"this date. Use it for fiscal year locking for example."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__fiscalyear_lock_date
|
||||
msgid ""
|
||||
"No users, including Advisers, can edit accounts prior to and inclusive of "
|
||||
"this date. Use it for fiscal year locking."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_res_config_settings__period_lock_date
|
||||
msgid ""
|
||||
"Only users with the 'Adviser' role can edit accounts prior to and inclusive "
|
||||
"of this date. Use it for period locking inside an open fiscal year, for "
|
||||
"example."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__period_lock_date
|
||||
msgid "Prevent posting of journal entries in this period."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "Show unposted entries"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__date_from
|
||||
msgid "Start Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_account_fiscal_year__date_from
|
||||
msgid "Start Date, included in the fiscal year."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__tax_lock_date
|
||||
msgid "Tax Lock Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/account_fiscal_year.py:0
|
||||
#, python-format
|
||||
msgid "The ending date must not be prior to the starting date."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"There are still unposted entries in the period you want to lock. You should "
|
||||
"either post or delete them."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"There are still unreconciled bank statement lines in the period you want to "
|
||||
"lock.You should either reconcile or delete them."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/wizard/change_lock_date.py:0
|
||||
#, python-format
|
||||
msgid "You Are Not Allowed To Perform This Operation"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/account_fiscal_year.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can not have an overlap between two fiscal years, please correct the "
|
||||
"start and/or end dates of your fiscal years."
|
||||
msgstr ""
|
||||
250
addons/om_fiscal_year/i18n/es_AR.po
Normal file
@@ -0,0 +1,250 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * om_fiscal_year
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 18.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-03-22 12:36+0000\n"
|
||||
"PO-Revision-Date: 2024-03-22 12:36+0000\n"
|
||||
"Last-Translator: Sergio Ariel Ameghino <ariel.ameghino@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Account Period Closing"
|
||||
msgstr "Cierre del período de la cuenta"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_lock_date
|
||||
msgid "All Users Lock Date"
|
||||
msgstr "Fecha de cierre"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:res.groups,name:om_fiscal_year.group_fiscal_year
|
||||
msgid "Allow to define fiscal years of more or less than a year"
|
||||
msgstr "Permitir definir años fiscales de más de 1 año o menos de 1 año"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_change_lock_date
|
||||
msgid "Change Lock Date"
|
||||
msgstr "Cambiar fecha de bloqueo"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.actions.act_window,help:om_fiscal_year.actions_account_fiscal_year
|
||||
msgid "Click here to create a new fiscal year."
|
||||
msgstr "Haga clic aquí para crear un nuevo año fiscal."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Compañías"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__company_id
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__company_id
|
||||
msgid "Company"
|
||||
msgstr "Compañía"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_res_config_settings
|
||||
msgid "Config Settings"
|
||||
msgstr "Ajustes de configuración"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__create_uid
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__create_uid
|
||||
msgid "Created by"
|
||||
msgstr "Creado por"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__create_date
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__create_date
|
||||
msgid "Created on"
|
||||
msgstr "Creado en"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Define fiscal years of more or less than one year"
|
||||
msgstr "Definir años fiscales de más de 1 año o menos de 1 año"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__display_name
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__display_name
|
||||
msgid "Display Name"
|
||||
msgstr "Nombre mostrado"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__date_to
|
||||
msgid "End Date"
|
||||
msgstr "Fecha final"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_account_fiscal_year__date_to
|
||||
msgid "Ending Date, included in the fiscal year."
|
||||
msgstr "Fecha de finalización, incluida en el ejercicio fiscal."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Period Closing"
|
||||
msgstr "Cierre del período fiscal"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_account_fiscal_year
|
||||
#: model:ir.ui.menu,name:om_fiscal_year.menu_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Year"
|
||||
msgstr "Año fiscal"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.actions.act_window,name:om_fiscal_year.actions_account_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__group_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Years"
|
||||
msgstr "Años fiscales"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_last_day
|
||||
msgid "Fiscalyear Last Day"
|
||||
msgstr "Año fiscal último día"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_last_month
|
||||
msgid "Fiscalyear Last Month"
|
||||
msgstr "Año fiscal último mes"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__id
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__id
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__period_lock_date
|
||||
msgid "Journal Entries Lock Date"
|
||||
msgstr "Fecha de bloqueo de asientos de diario"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__period_lock_date
|
||||
msgid "Journals Entries Lock Date"
|
||||
msgstr "Fecha de bloqueo para no asesores"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Last Day"
|
||||
msgstr "Último día"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__write_uid
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr "Última actualización por"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__write_date
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr "Última actualización en"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__fiscalyear_lock_date
|
||||
msgid "Lock Date for All Users"
|
||||
msgstr "Fecha de bloqueo para todos los usuarios"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.ui.menu,name:om_fiscal_year.menu_action_change_lock_date
|
||||
msgid "Lock Dates"
|
||||
msgstr "Fechas de bloqueo"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.actions.act_window,name:om_fiscal_year.action_view_change_lock_date
|
||||
msgid "Lock your Fiscal Period"
|
||||
msgstr "Bloqueo de su período fiscal"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Lock your fiscal period"
|
||||
msgstr "Bloqueo de su período fiscal"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Management Closing"
|
||||
msgstr "Cierre de gestión"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__name
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__tax_lock_date
|
||||
msgid ""
|
||||
"No users can edit journal entries related to a tax prior and inclusive of "
|
||||
"this date."
|
||||
msgstr ""
|
||||
"Ningún usuario puede editar asientos de diario relacionados con un impuesto "
|
||||
"anterior e inclusive de esta fecha."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_res_config_settings__fiscalyear_lock_date
|
||||
msgid ""
|
||||
"No users, including Advisers, can edit accounts prior to and inclusive of "
|
||||
"this date. Use it for fiscal year locking for example."
|
||||
msgstr ""
|
||||
"Ningún usuario, incluidos los asesores, puede editar cuentas antes de esta "
|
||||
"fecha inclusive. Úselo para bloquear el año fiscal por ejemplo."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__fiscalyear_lock_date
|
||||
msgid ""
|
||||
"No users, including Advisers, can edit accounts prior to and inclusive of "
|
||||
"this date. Use it for fiscal year locking."
|
||||
msgstr ""
|
||||
"Ningún usuario, incluidos los Asesores, puede editar cuentas antes de esta "
|
||||
"fecha inclusive. Úselo para bloquear el año fiscal."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_res_config_settings__period_lock_date
|
||||
msgid ""
|
||||
"Only users with the 'Adviser' role can edit accounts prior to and inclusive "
|
||||
"of this date. Use it for period locking inside an open fiscal year, for "
|
||||
"example."
|
||||
msgstr ""
|
||||
"Solo los usuarios con el rol de 'Asesor' pueden editar cuentas antes de esta"
|
||||
" fecha inclusive. Úselo para el bloqueo de períodos dentro de un año fiscal "
|
||||
"abierto, por ejemplo."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__period_lock_date
|
||||
msgid "Prevent posting of journal entries in this period."
|
||||
msgstr "Evitar la contabilización de asientos de diario en este período."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Save"
|
||||
msgstr "Guardar"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__date_from
|
||||
msgid "Start Date"
|
||||
msgstr "Fecha de inicio"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_account_fiscal_year__date_from
|
||||
msgid "Start Date, included in the fiscal year."
|
||||
msgstr "Fecha de inicio, incluida en el ejercicio fiscal."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__tax_lock_date
|
||||
msgid "Tax Lock Date"
|
||||
msgstr "Fecha de bloqueo de impuestos"
|
||||
309
addons/om_fiscal_year/i18n/fr.po
Normal file
@@ -0,0 +1,309 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * om_fiscal_year
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 15.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-04-15 06:46+0000\n"
|
||||
"PO-Revision-Date: 2022-07-06 00:19+0200\n"
|
||||
"Last-Translator: Sylvain Lc\n"
|
||||
"Language-Team: \n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"X-Generator: Poedit 3.1\n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Account Period Closing"
|
||||
msgstr "Clôture de la période du compte"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:res.groups,name:om_fiscal_year.group_fiscal_year
|
||||
msgid "Allow to define fiscal years of more or less than a year"
|
||||
msgstr "Permettre de définir des années fiscales de plus ou moins d'un an"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_change_lock_date
|
||||
msgid "Change Lock Date"
|
||||
msgstr "Changer la date de cloture"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.actions.act_window,help:om_fiscal_year.actions_account_fiscal_year
|
||||
msgid "Click here to create a new fiscal year."
|
||||
msgstr "Cliquez ici pour créer une nouvelle année fiscale."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Sociétés"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__company_id
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__company_id
|
||||
msgid "Company"
|
||||
msgstr "Société"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_res_config_settings
|
||||
msgid "Config Settings"
|
||||
msgstr "Paramètres de Configuration"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__create_uid
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__create_uid
|
||||
msgid "Created by"
|
||||
msgstr "Créé par"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__create_date
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__create_date
|
||||
msgid "Created on"
|
||||
msgstr "Créé sur"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Define fiscal years of more or less than one year"
|
||||
msgstr "Définir des exercices de plus ou de moins d'un an"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__display_name
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__display_name
|
||||
msgid "Display Name"
|
||||
msgstr "Afficher un nom"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__date_to
|
||||
msgid "End Date"
|
||||
msgstr "Date de fin"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_account_fiscal_year__date_to
|
||||
msgid "Ending Date, included in the fiscal year."
|
||||
msgstr "Date de fin, comprise dans l'année fiscale."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Period Closing"
|
||||
msgstr "Clôture de l’exercice"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_account_fiscal_year
|
||||
#: model:ir.ui.menu,name:om_fiscal_year.menu_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Year"
|
||||
msgstr "Exercice fiscal"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.actions.act_window,name:om_fiscal_year.actions_account_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__group_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Years"
|
||||
msgstr "Exercices fiscaux"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_last_day
|
||||
msgid "Fiscalyear Last Day"
|
||||
msgstr "Dernier jour de l'exercice fiscal"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_last_month
|
||||
msgid "Fiscalyear Last Month"
|
||||
msgstr "Dernier mois de l'exercice fiscal"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__id
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__id
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__period_lock_date
|
||||
msgid "Journal Entries Lock Date"
|
||||
msgstr "Date de verrouillage des entrées de journal"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Last Day"
|
||||
msgstr "Dernier jour"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year____last_update
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date____last_update
|
||||
msgid "Last Modified on"
|
||||
msgstr "Dernière modification le"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__write_uid
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr "Dernière mise à jour par"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__write_date
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr "Dernière mise à jour le"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_lock_date
|
||||
msgid "Lock Date"
|
||||
msgstr "Date de verrouillage"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__fiscalyear_lock_date
|
||||
msgid "Lock Date for All Users"
|
||||
msgstr "Date de clôture pour tous les utilisateurs"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__period_lock_date
|
||||
msgid "Lock Date for Non-Advisers"
|
||||
msgstr "Date de verrouillage pour les non-conseillers"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.ui.menu,name:om_fiscal_year.menu_action_change_lock_date
|
||||
msgid "Lock Dates"
|
||||
msgstr "Date de clôture"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.actions.act_window,name:om_fiscal_year.action_view_change_lock_date
|
||||
msgid "Lock your Fiscal Period"
|
||||
msgstr "Clôturer la période fiscale"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Lock your fiscal period"
|
||||
msgstr "Clôturer la période fiscale"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Management Closing"
|
||||
msgstr "Clôture de la direction"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__name
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__tax_lock_date
|
||||
msgid ""
|
||||
"No users can edit journal entries related to a tax prior and inclusive of "
|
||||
"this date."
|
||||
msgstr ""
|
||||
"Aucun utilisateur ne peut modifier des pièces comptables relatives à une "
|
||||
"taxe avant et au jour de cette date."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_res_config_settings__fiscalyear_lock_date
|
||||
msgid ""
|
||||
"No users, including Advisers, can edit accounts prior to and inclusive of "
|
||||
"this date. Use it for fiscal year locking for example."
|
||||
msgstr ""
|
||||
"Aucun utilisateur, y compris les conseillers, ne peuvent modifier les "
|
||||
"comptes comptables avant cette date inclusivement. Utilisez cela pour "
|
||||
"verrouiller l'exercice fiscal par exemple."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__fiscalyear_lock_date
|
||||
msgid ""
|
||||
"No users, including Advisers, can edit accounts prior to and inclusive of "
|
||||
"this date. Use it for fiscal year locking."
|
||||
msgstr ""
|
||||
"Aucun utilisateur, y compris les conseillers, ne peuvent modifier les "
|
||||
"comptes comptables avant cette date inclusivement. Utilisez cela pour "
|
||||
"verrouiller l'exercice fiscal par exemple."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_res_config_settings__period_lock_date
|
||||
msgid ""
|
||||
"Only users with the 'Adviser' role can edit accounts prior to and inclusive "
|
||||
"of this date. Use it for period locking inside an open fiscal year, for "
|
||||
"example."
|
||||
msgstr ""
|
||||
"Seuls les utilisateurs avec le rôle 'Conseiller' peuvent modifier les "
|
||||
"comptes comptables avant cette date inclusivement. Utilisez cela pour "
|
||||
"verrouiller des périodes pendant l'exercice fiscal par exemple."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__period_lock_date
|
||||
msgid "Prevent posting of journal entries in this period."
|
||||
msgstr "Empêcher la publication d’entrées de journal pendant cette période."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Save"
|
||||
msgstr "Sauvegarder"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "Show unposted entries"
|
||||
msgstr "Montrer les Ecritures non Postées"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__date_from
|
||||
msgid "Start Date"
|
||||
msgstr "Date de début"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_account_fiscal_year__date_from
|
||||
msgid "Start Date, included in the fiscal year."
|
||||
msgstr "Date de début, incluse dans l'année fiscale."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__tax_lock_date
|
||||
msgid "Tax Lock Date"
|
||||
msgstr "Date de verrouillage de la taxe"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/account_fiscal_year.py:0
|
||||
#, python-format
|
||||
msgid "The ending date must not be prior to the starting date."
|
||||
msgstr "La date de fin ne peut être antérieure à la date de début."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"There are still unposted entries in the period you want to lock. You should "
|
||||
"either post or delete them."
|
||||
msgstr ""
|
||||
"Il y a toujours des entrées non comptabilisées dans la période que vous "
|
||||
"souhaitez verrouiller. Vous devez les comptabiliser ou les supprimer."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"There are still unreconciled bank statement lines in the period you want to "
|
||||
"lock.You should either reconcile or delete them."
|
||||
msgstr ""
|
||||
"Il reste des lignes de relevé bancaire non rapprochées dans la période que "
|
||||
"vous souhaitez verrouiller. Vous devez soit les rapprocher, soit les "
|
||||
"supprimer."
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/wizard/change_lock_date.py:0
|
||||
#, python-format
|
||||
msgid "You Are Not Allowed To Perform This Operation"
|
||||
msgstr "Vous n’êtes pas autorisé à effectuer cette opération"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/account_fiscal_year.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can not have an overlap between two fiscal years, please correct the "
|
||||
"start and/or end dates of your fiscal years."
|
||||
msgstr ""
|
||||
"Vous ne pouvez pas avoir de chevauchement entre deux exercices, veuillez "
|
||||
"corriger les dates de début et/ou de fin de vos exercices."
|
||||
289
addons/om_fiscal_year/i18n/tr.po
Normal file
@@ -0,0 +1,289 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * om_fiscal_year
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 15.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-04-15 06:46+0000\n"
|
||||
"PO-Revision-Date: 2022-04-15 06:46+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Account Period Closing"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:res.groups,name:om_fiscal_year.group_fiscal_year
|
||||
msgid "Allow to define fiscal years of more or less than a year"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Cancel"
|
||||
msgstr "İptal"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_change_lock_date
|
||||
msgid "Change Lock Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.actions.act_window,help:om_fiscal_year.actions_account_fiscal_year
|
||||
msgid "Click here to create a new fiscal year."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Şirketler"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__company_id
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__company_id
|
||||
msgid "Company"
|
||||
msgstr "Şirket"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_res_config_settings
|
||||
msgid "Config Settings"
|
||||
msgstr "Yapılandırma Ayarları"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__create_uid
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__create_uid
|
||||
msgid "Created by"
|
||||
msgstr "Oluşturan"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__create_date
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__create_date
|
||||
msgid "Created on"
|
||||
msgstr "Oluşturulma Tarihi"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Define fiscal years of more or less than one year"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__display_name
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__display_name
|
||||
msgid "Display Name"
|
||||
msgstr "Görüntülenen Ad"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__date_to
|
||||
msgid "End Date"
|
||||
msgstr "Bitiş Tarihi"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_account_fiscal_year__date_to
|
||||
msgid "Ending Date, included in the fiscal year."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Period Closing"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_account_fiscal_year
|
||||
#: model:ir.ui.menu,name:om_fiscal_year.menu_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Year"
|
||||
msgstr "Hesap Yılı"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.actions.act_window,name:om_fiscal_year.actions_account_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__group_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Years"
|
||||
msgstr "Hesap Yılları"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_last_day
|
||||
msgid "Fiscalyear Last Day"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_last_month
|
||||
msgid "Fiscalyear Last Month"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__id
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__id
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__period_lock_date
|
||||
msgid "Journal Entries Lock Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Last Day"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year____last_update
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date____last_update
|
||||
msgid "Last Modified on"
|
||||
msgstr "Son Değişiklik Tarihi"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__write_uid
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr "Son Güncellemeyi Yapan"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__write_date
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr "Son Güncelleme Tarihi"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_lock_date
|
||||
msgid "Lock Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__fiscalyear_lock_date
|
||||
msgid "Lock Date for All Users"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__period_lock_date
|
||||
msgid "Lock Date for Non-Advisers"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.ui.menu,name:om_fiscal_year.menu_action_change_lock_date
|
||||
msgid "Lock Dates"
|
||||
msgstr "Kilitleme Tarihleri"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.actions.act_window,name:om_fiscal_year.action_view_change_lock_date
|
||||
msgid "Lock your Fiscal Period"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Lock your fiscal period"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Management Closing"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__name
|
||||
msgid "Name"
|
||||
msgstr "İsim"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__tax_lock_date
|
||||
msgid ""
|
||||
"No users can edit journal entries related to a tax prior and inclusive of "
|
||||
"this date."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_res_config_settings__fiscalyear_lock_date
|
||||
msgid ""
|
||||
"No users, including Advisers, can edit accounts prior to and inclusive of "
|
||||
"this date. Use it for fiscal year locking for example."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__fiscalyear_lock_date
|
||||
msgid ""
|
||||
"No users, including Advisers, can edit accounts prior to and inclusive of "
|
||||
"this date. Use it for fiscal year locking."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_res_config_settings__period_lock_date
|
||||
msgid ""
|
||||
"Only users with the 'Adviser' role can edit accounts prior to and inclusive "
|
||||
"of this date. Use it for period locking inside an open fiscal year, for "
|
||||
"example."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__period_lock_date
|
||||
msgid "Prevent posting of journal entries in this period."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "Show unposted entries"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__date_from
|
||||
msgid "Start Date"
|
||||
msgstr "Başlangıç Tarihi"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_account_fiscal_year__date_from
|
||||
msgid "Start Date, included in the fiscal year."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__tax_lock_date
|
||||
msgid "Tax Lock Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/account_fiscal_year.py:0
|
||||
#, python-format
|
||||
msgid "The ending date must not be prior to the starting date."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"There are still unposted entries in the period you want to lock. You should "
|
||||
"either post or delete them."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"There are still unreconciled bank statement lines in the period you want to "
|
||||
"lock.You should either reconcile or delete them."
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/wizard/change_lock_date.py:0
|
||||
#, python-format
|
||||
msgid "You Are Not Allowed To Perform This Operation"
|
||||
msgstr ""
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: code:addons/om_fiscal_year/models/account_fiscal_year.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can not have an overlap between two fiscal years, please correct the "
|
||||
"start and/or end dates of your fiscal years."
|
||||
msgstr ""
|
||||
289
addons/om_fiscal_year/i18n/zh_TW.po
Normal file
@@ -0,0 +1,289 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * om_fiscal_year
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 18.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-03-21 02:42+0000\n"
|
||||
"PO-Revision-Date: 2024-03-21 02:42+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Account Period Closing"
|
||||
msgstr "會計關帳日期"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_lock_date
|
||||
msgid "Lock Date for All Users"
|
||||
msgstr "所有使用者關帳日期"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:res.groups,name:om_fiscal_year.group_fiscal_year
|
||||
msgid "Allow to define fiscal years of more or less than a year"
|
||||
msgstr "允許定義多於或少於一年的會計年度"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Cancel"
|
||||
msgstr "取消"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_change_lock_date
|
||||
msgid "Change Lock Date"
|
||||
msgstr "更改關帳日期"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.actions.act_window,help:om_fiscal_year.actions_account_fiscal_year
|
||||
msgid "Click here to create a new fiscal year."
|
||||
msgstr "點選新增會計年度"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "公司"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__company_id
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__company_id
|
||||
msgid "Company"
|
||||
msgstr "公司"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_res_config_settings
|
||||
msgid "Config Settings"
|
||||
msgstr "配置設定"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__create_uid
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__create_uid
|
||||
msgid "Created by"
|
||||
msgstr "建立於"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__create_date
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__create_date
|
||||
msgid "Created on"
|
||||
msgstr "建立於"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Define fiscal years of more or less than one year"
|
||||
msgstr "定義多於或少於一年的會計年度"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__display_name
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__display_name
|
||||
msgid "Display Name"
|
||||
msgstr "顯示名稱"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__date_to
|
||||
msgid "End Date"
|
||||
msgstr "結束日期"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_account_fiscal_year__date_to
|
||||
msgid "Ending Date, included in the fiscal year."
|
||||
msgstr "結束日期,包含在會計年度中"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Period Closing"
|
||||
msgstr "會計關帳期間"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model,name:om_fiscal_year.model_account_fiscal_year
|
||||
#: model:ir.ui.menu,name:om_fiscal_year.menu_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Year"
|
||||
msgstr "會計年度"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.actions.act_window,name:om_fiscal_year.actions_account_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__group_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Fiscal Years"
|
||||
msgstr "會計年度"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_last_day
|
||||
msgid "Fiscalyear Last Day"
|
||||
msgstr "會計年度最後一天"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__fiscalyear_last_month
|
||||
msgid "Fiscalyear Last Month"
|
||||
msgstr "會計年度最後一月"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__id
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__id
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__period_lock_date
|
||||
msgid "Journal Entries Lock Date"
|
||||
msgstr "日記帳本關帳日期"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_res_config_settings__period_lock_date
|
||||
msgid "Journals Entries Lock Date"
|
||||
msgstr "日記帳本關帳日期"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Last Day"
|
||||
msgstr "最後一天"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__write_uid
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr "最後更新"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__write_date
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr "最後更新"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__fiscalyear_lock_date
|
||||
msgid "Lock Date for All Users"
|
||||
msgstr "所有使用者關帳日期"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.ui.menu,name:om_fiscal_year.menu_action_change_lock_date
|
||||
msgid "Lock Dates"
|
||||
msgstr "會計關帳日期"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.actions.act_window,name:om_fiscal_year.action_view_change_lock_date
|
||||
msgid "Lock your Fiscal Period"
|
||||
msgstr "會計關帳期間"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.res_config_settings_view_form
|
||||
msgid "Lock your fiscal period"
|
||||
msgstr "會計關帳期間"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Management Closing"
|
||||
msgstr "管理關帳日期"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__name
|
||||
msgid "Name"
|
||||
msgstr "名稱"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__tax_lock_date
|
||||
msgid ""
|
||||
"No users can edit journal entries related to a tax prior and inclusive of "
|
||||
"this date."
|
||||
msgstr "任何使用者都無法編輯與該日期之前(含該日期)的稅務相關的日記帳分錄。"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_res_config_settings__fiscalyear_lock_date
|
||||
msgid ""
|
||||
"No users, including Advisers, can edit accounts prior to and inclusive of "
|
||||
"this date. Use it for fiscal year locking for example."
|
||||
msgstr "任何使用者(包括管理者)都不能在「之前」(含)編輯帳戶這個日期。例如,將其用於會計年度關帳。”"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__fiscalyear_lock_date
|
||||
msgid ""
|
||||
"No users, including Advisers, can edit accounts prior to and inclusive of "
|
||||
"this date. Use it for fiscal year locking."
|
||||
msgstr "在此日期之前(包括該日期),任何使用者(包括管理者)都不能編輯帳戶。 使用它來關帳會計年度。"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_res_config_settings__period_lock_date
|
||||
msgid ""
|
||||
"Only users with the 'Adviser' role can edit accounts prior to and inclusive "
|
||||
"of this date. Use it for period locking inside an open fiscal year, for "
|
||||
"example."
|
||||
msgstr "只有具有「管理者」角色的使用者才能在此日期之前(含該日期)編輯帳戶。 例如,將其用於開放會計年度內的期間關帳。"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_change_lock_date__period_lock_date
|
||||
msgid "Prevent posting of journal entries in this period."
|
||||
msgstr "防止在此期間過帳日記帳分錄"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model_terms:ir.ui.view,arch_db:om_fiscal_year.view_change_lock_date
|
||||
msgid "Save"
|
||||
msgstr "保存"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#. odoo-python
|
||||
#: code:addons/om_fiscal_year/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "Show unposted entries"
|
||||
msgstr "顯示未過帳的分錄"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_account_fiscal_year__date_from
|
||||
msgid "Start Date"
|
||||
msgstr "開始日期"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,help:om_fiscal_year.field_account_fiscal_year__date_from
|
||||
msgid "Start Date, included in the fiscal year."
|
||||
msgstr "開始日期,包含在會計年度中。"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#: model:ir.model.fields,field_description:om_fiscal_year.field_change_lock_date__tax_lock_date
|
||||
msgid "Tax Lock Date"
|
||||
msgstr "稅額關帳日期"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#. odoo-python
|
||||
#: code:addons/om_fiscal_year/models/account_fiscal_year.py:0
|
||||
#, python-format
|
||||
msgid "The ending date must not be prior to the starting date."
|
||||
msgstr "結束日期不得早於開始日期。"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#. odoo-python
|
||||
#: code:addons/om_fiscal_year/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"There are still unposted entries in the period you want to lock. You should "
|
||||
"either post or delete them."
|
||||
msgstr "在您想要關帳的期間內仍有未過帳的條目。 您應該發布或刪除它們。"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#. odoo-python
|
||||
#: code:addons/om_fiscal_year/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"There are still unreconciled bank statement lines in the period you want to "
|
||||
"lock.You should either reconcile or delete them."
|
||||
msgstr "您想要關帳的期間內仍有未調整的銀行對帳單行。您應該調整或刪除它們"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#. odoo-python
|
||||
#: code:addons/om_fiscal_year/wizard/change_lock_date.py:0
|
||||
#, python-format
|
||||
msgid "You Are Not Allowed To Perform This Operation"
|
||||
msgstr "您無權執行此操作"
|
||||
|
||||
#. module: om_fiscal_year
|
||||
#. odoo-python
|
||||
#: code:addons/om_fiscal_year/models/account_fiscal_year.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You can not have an overlap between two fiscal years, please correct the "
|
||||
"start and/or end dates of your fiscal years."
|
||||
msgstr "兩個會計年度之間不能有重疊,請更正會計年度的開始和/或結束日期。"
|
||||
3
addons/om_fiscal_year/models/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from . import account_fiscal_year
|
||||
from . import account_settings
|
||||
from . import res_company
|
||||
48
addons/om_fiscal_year/models/account_fiscal_year.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class AccountFiscalYear(models.Model):
|
||||
_name = 'account.fiscal.year'
|
||||
_description = 'Fiscal Year'
|
||||
|
||||
name = fields.Char(string='Name', required=True)
|
||||
date_from = fields.Date(string='Start Date', required=True,
|
||||
help='Start Date, included in the fiscal year.')
|
||||
date_to = fields.Date(string='End Date', required=True,
|
||||
help='Ending Date, included in the fiscal year.')
|
||||
company_id = fields.Many2one('res.company', string='Company', required=True,
|
||||
default=lambda self: self.env.company)
|
||||
|
||||
@api.constrains('date_from', 'date_to', 'company_id')
|
||||
def _check_dates(self):
|
||||
'''
|
||||
Check interleaving between fiscal years.
|
||||
There are 3 cases to consider:
|
||||
|
||||
s1 s2 e1 e2
|
||||
( [----)----]
|
||||
|
||||
s2 s1 e2 e1
|
||||
[----(----] )
|
||||
|
||||
s1 s2 e2 e1
|
||||
( [----] )
|
||||
'''
|
||||
for fy in self:
|
||||
# Starting date must be prior to the ending date
|
||||
date_from = fy.date_from
|
||||
date_to = fy.date_to
|
||||
if date_to < date_from:
|
||||
raise ValidationError(_('The ending date must not be prior to the starting date.'))
|
||||
domain = [
|
||||
('id', '!=', fy.id),
|
||||
('company_id', '=', fy.company_id.id),
|
||||
'|', '|',
|
||||
'&', ('date_from', '<=', fy.date_from), ('date_to', '>=', fy.date_from),
|
||||
'&', ('date_from', '<=', fy.date_to), ('date_to', '>=', fy.date_to),
|
||||
'&', ('date_from', '<=', fy.date_from), ('date_to', '>=', fy.date_to),
|
||||
]
|
||||
if self.search_count(domain) > 0:
|
||||
raise ValidationError(_('You can not have an overlap between two fiscal years, '
|
||||
'please correct the start and/or end dates of your fiscal years.'))
|
||||
30
addons/om_fiscal_year/models/account_settings.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
fiscalyear_last_day = fields.Integer(
|
||||
related='company_id.fiscalyear_last_day', readonly=False
|
||||
)
|
||||
fiscalyear_last_month = fields.Selection(
|
||||
related='company_id.fiscalyear_last_month', readonly=False
|
||||
)
|
||||
tax_lock_date = fields.Date(
|
||||
related='company_id.hard_lock_date', readonly=False
|
||||
)
|
||||
sale_lock_date = fields.Date(
|
||||
related='company_id.hard_lock_date', readonly=False
|
||||
)
|
||||
purchase_lock_date = fields.Date(
|
||||
related='company_id.hard_lock_date', readonly=False
|
||||
)
|
||||
hard_lock_date = fields.Date(
|
||||
related='company_id.hard_lock_date', readonly=False
|
||||
)
|
||||
fiscalyear_lock_date = fields.Date(
|
||||
related='company_id.fiscalyear_lock_date', readonly=False
|
||||
)
|
||||
group_fiscal_year = fields.Boolean(
|
||||
string='Fiscal Years', implied_group='om_fiscal_year.group_fiscal_year'
|
||||
)
|
||||
38
addons/om_fiscal_year/models/res_company.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import RedirectWarning, ValidationError
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
def _validate_fiscalyear_lock(self, values):
|
||||
if values.get('fiscalyear_lock_date'):
|
||||
draft_entries = self.env['account.move'].search([
|
||||
('company_id', 'in', self.ids),
|
||||
('state', '=', 'draft'),
|
||||
('date', '<=', values['fiscalyear_lock_date'])])
|
||||
if draft_entries:
|
||||
error_msg = _(
|
||||
'There are still unposted entries in the period you want to lock. You should either post or delete them.')
|
||||
action_error = {
|
||||
'view_mode': 'list',
|
||||
'name': 'Unposted Entries',
|
||||
'res_model': 'account.move',
|
||||
'type': 'ir.actions.act_window',
|
||||
'domain': [('id', 'in', draft_entries.ids)],
|
||||
'search_view_id': [self.env.ref('account.view_account_move_filter').id, 'search'],
|
||||
'views': [[self.env.ref('account.view_move_tree').id, 'list'],
|
||||
[self.env.ref('account.view_move_form').id, 'form']],
|
||||
}
|
||||
raise RedirectWarning(error_msg, action_error, _('Show unposted entries'))
|
||||
|
||||
unreconciled_statement_lines = self.env['account.bank.statement.line'].search([
|
||||
('company_id', 'in', self.ids),
|
||||
('is_reconciled', '=', False),
|
||||
('date', '<=', values['fiscalyear_lock_date']),
|
||||
('move_id.state', 'in', ('draft', 'posted')),
|
||||
])
|
||||
if unreconciled_statement_lines:
|
||||
error_msg = _("There are still unreconciled bank statement lines in the period you want to lock."
|
||||
"You should either reconcile or delete them.")
|
||||
raise ValidationError(error_msg)
|
||||
5
addons/om_fiscal_year/security/ir.model.access.csv
Normal file
@@ -0,0 +1,5 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_account_fiscal_year,access.account.fiscal.year,model_account_fiscal_year,account.group_account_user,1,1,0,0
|
||||
access_account_fiscal_year_manager,access.account.fiscal.year.manager,model_account_fiscal_year,account.group_account_manager,1,1,1,1
|
||||
access_change_lock_date_manager,access.change.lock.date.manager,model_change_lock_date,account.group_account_manager,1,1,1,1
|
||||
|
||||
|
11
addons/om_fiscal_year/security/security.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="0">
|
||||
|
||||
<record id="group_fiscal_year" model="res.groups">
|
||||
<field name="name">Allow to define fiscal years of more or less than a year</field>
|
||||
<field name="user_ids" eval="[Command.link(ref('base.user_root')), Command.link(ref('base.user_admin'))]"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
BIN
addons/om_fiscal_year/static/description/fiscal_year.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
addons/om_fiscal_year/static/description/icon.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
60
addons/om_fiscal_year/static/description/index.html
Normal file
@@ -0,0 +1,60 @@
|
||||
<section class="oe_container oe_dark">
|
||||
<div class="col-md-12">
|
||||
<h2 class="oe_slogan" style="font-size: 35px;color:#2C0091"><b>Odoo 19 Fiscal Year & Closing</b></h2>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<div class="oe_span12">
|
||||
<h3 class="oe_slogan" style="color:#332c3c;font-size: 28px;">Fiscal Year</h3>
|
||||
<div class="oe_demo oe_picture oe_screenshot">
|
||||
<img src="fiscal_year.png" style="height:400px;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<div class="oe_span12">
|
||||
<h3 class="oe_slogan" style="color:#332c3c;font-size: 28px;">Lock Date</h3>
|
||||
<div class="oe_demo oe_picture oe_screenshot">
|
||||
<img src="lock_date.png" style="height:400px;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<hr style="width: 100%;height: 4px;background: #2C0091;margin: 0px 0px;">
|
||||
<hr style="width: 100%;height: 4px;background: #148963;margin: 0px 0px;">
|
||||
<section class="oe_container oe_dark">
|
||||
<div class="oe_row ">
|
||||
<div class="oe_slogan text-center">
|
||||
<img src="odoo_mates.png"/>
|
||||
<div style="color:#269900;">
|
||||
<h3 style="color:#2C0091;font-size: 25px;">If you need any help or want more features, just contact us:</h3><br>
|
||||
<h3 style="color:#2C0091;font-size: 20px;">Email: <a href="odoomates@gmail.com">odoomates@gmail.com</a> <br></h3>
|
||||
</div>
|
||||
<div class="oe_slogan">
|
||||
<h2>
|
||||
<a target="_blank" href="https://www.facebook.com/odoomate/" target="new">
|
||||
<i class="fa fa-facebook-square" style="font-size:38px;"></i>
|
||||
</a>
|
||||
<a target="_blank" href="https://twitter.com/odoomates/" target="new">
|
||||
<i class="fa fa-twitter" style="font-size:38px;"></i>
|
||||
</a>
|
||||
<a href="#" target="_blank">
|
||||
<i class="fa fa-linkedin" style="font-size:38px;"></i>
|
||||
</a>
|
||||
<a target="_blank" href="https://www.youtube.com/channel/UCVKlUZP7HAhdQgs-9iTJklQ">
|
||||
<i class="fa fa-youtube-play" style="font-size:38px;"></i>
|
||||
</a>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<hr style="width: 100%;height: 4px;background: #148963;margin: 0px 0px;">
|
||||
<hr style="width: 100%;height: 4px;background: #2C0091;margin: 0px 0px;">
|
||||
BIN
addons/om_fiscal_year/static/description/lock_date.png
Normal file
|
After Width: | Height: | Size: 73 KiB |