odoo-addon-account-move-payroll-import 16.0.1.0.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odoo/addons/account_move_payroll_import/CHANGELOG.md +14 -0
- odoo/addons/account_move_payroll_import/README.rst +117 -0
- odoo/addons/account_move_payroll_import/__init__.py +4 -0
- odoo/addons/account_move_payroll_import/__manifest__.py +39 -0
- odoo/addons/account_move_payroll_import/data/payroll_import_defaults.xml +108 -0
- odoo/addons/account_move_payroll_import/i18n/ca_ES.po +828 -0
- odoo/addons/account_move_payroll_import/i18n/es.po +829 -0
- odoo/addons/account_move_payroll_import/models/__init__.py +6 -0
- odoo/addons/account_move_payroll_import/models/account_move.py +167 -0
- odoo/addons/account_move_payroll_import/models/payroll_custom_concept.py +57 -0
- odoo/addons/account_move_payroll_import/models/payroll_import_mapping.py +28 -0
- odoo/addons/account_move_payroll_import/models/payroll_import_setup.py +497 -0
- odoo/addons/account_move_payroll_import/security/ir.model.access.csv +5 -0
- odoo/addons/account_move_payroll_import/static/src/css/styles.css +26 -0
- odoo/addons/account_move_payroll_import/static/src/js/payroll_import_button.js +32 -0
- odoo/addons/account_move_payroll_import/static/src/xml/payroll_import_templates.xml +24 -0
- odoo/addons/account_move_payroll_import/utils/__init__.py +0 -0
- odoo/addons/account_move_payroll_import/utils/file_utils.py +31 -0
- odoo/addons/account_move_payroll_import/utils/parse_utils.py +34 -0
- odoo/addons/account_move_payroll_import/views/assets_template.xml +13 -0
- odoo/addons/account_move_payroll_import/views/payroll_import_views.xml +229 -0
- odoo/addons/account_move_payroll_import/wizards/__init__.py +3 -0
- odoo/addons/account_move_payroll_import/wizards/payroll_import_wizard.py +320 -0
- odoo/addons/account_move_payroll_import/wizards/payroll_import_wizard.xml +44 -0
- odoo_addon_account_move_payroll_import-16.0.1.0.0.dist-info/METADATA +131 -0
- odoo_addon_account_move_payroll_import-16.0.1.0.0.dist-info/RECORD +28 -0
- odoo_addon_account_move_payroll_import-16.0.1.0.0.dist-info/WHEEL +6 -0
- odoo_addon_account_move_payroll_import-16.0.1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
|
|
4
|
+
from odoo import models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AccountMove(models.Model):
|
|
8
|
+
_inherit = "account.move"
|
|
9
|
+
|
|
10
|
+
@contextmanager
|
|
11
|
+
def _sync_dynamic_line( # noqa: C901
|
|
12
|
+
self,
|
|
13
|
+
existing_key_fname,
|
|
14
|
+
needed_vals_fname,
|
|
15
|
+
needed_dirty_fname,
|
|
16
|
+
line_type,
|
|
17
|
+
container,
|
|
18
|
+
):
|
|
19
|
+
"""
|
|
20
|
+
Overwrite this method to prevent line modifications in payroll import context
|
|
21
|
+
See account/account_move.py for the original method.
|
|
22
|
+
"""
|
|
23
|
+
def existing():
|
|
24
|
+
return {
|
|
25
|
+
line: line[existing_key_fname]
|
|
26
|
+
for line in container["records"].line_ids
|
|
27
|
+
if line[existing_key_fname]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
def needed():
|
|
31
|
+
res = {}
|
|
32
|
+
for computed_needed in container["records"].mapped(needed_vals_fname):
|
|
33
|
+
if computed_needed is False:
|
|
34
|
+
continue # there was an invalidation, let's hope nothing needed to be changed...
|
|
35
|
+
for key, values in computed_needed.items():
|
|
36
|
+
if key not in res:
|
|
37
|
+
res[key] = dict(values)
|
|
38
|
+
else:
|
|
39
|
+
ignore = True
|
|
40
|
+
for fname in res[key]:
|
|
41
|
+
if (
|
|
42
|
+
self.env["account.move.line"]._fields[fname].type
|
|
43
|
+
== "monetary"
|
|
44
|
+
):
|
|
45
|
+
res[key][fname] += values[fname]
|
|
46
|
+
if res[key][fname]:
|
|
47
|
+
ignore = False
|
|
48
|
+
if ignore:
|
|
49
|
+
del res[key]
|
|
50
|
+
|
|
51
|
+
# Convert float values to their "ORM cache" one to prevent different rounding calculations
|
|
52
|
+
for dict_key in res:
|
|
53
|
+
move_id = dict_key.get("move_id")
|
|
54
|
+
if not move_id:
|
|
55
|
+
continue
|
|
56
|
+
record = self.env["account.move"].browse(move_id)
|
|
57
|
+
for fname, current_value in res[dict_key].items():
|
|
58
|
+
field = self.env["account.move.line"]._fields[fname]
|
|
59
|
+
if isinstance(current_value, float):
|
|
60
|
+
new_value = field.convert_to_cache(current_value, record)
|
|
61
|
+
res[dict_key][fname] = new_value
|
|
62
|
+
|
|
63
|
+
return res
|
|
64
|
+
|
|
65
|
+
def dirty():
|
|
66
|
+
*path, dirty_fname = needed_dirty_fname.split(".")
|
|
67
|
+
eligible_recs = container["records"].mapped(".".join(path))
|
|
68
|
+
if eligible_recs._name == "account.move.line":
|
|
69
|
+
eligible_recs = eligible_recs.filtered(
|
|
70
|
+
lambda l: l.display_type != "cogs"
|
|
71
|
+
)
|
|
72
|
+
dirty_recs = eligible_recs.filtered(dirty_fname)
|
|
73
|
+
return dirty_recs, dirty_fname
|
|
74
|
+
|
|
75
|
+
# don't do anything if we are in a payroll import context
|
|
76
|
+
if self.env.context.get("is_payroll_import"):
|
|
77
|
+
yield
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
inv_existing_before = existing()
|
|
81
|
+
needed_before = needed()
|
|
82
|
+
dirty_recs_before, dirty_fname = dirty()
|
|
83
|
+
dirty_recs_before[dirty_fname] = False
|
|
84
|
+
yield
|
|
85
|
+
dirty_recs_after, dirty_fname = dirty()
|
|
86
|
+
if dirty_recs_before and not dirty_recs_after: # TODO improve filter
|
|
87
|
+
return
|
|
88
|
+
inv_existing_after = existing()
|
|
89
|
+
needed_after = needed()
|
|
90
|
+
|
|
91
|
+
# Filter out deleted lines from `needed_before` to not recompute lines if not necessary or wanted
|
|
92
|
+
line_ids = set(
|
|
93
|
+
self.env["account.move.line"]
|
|
94
|
+
.browse(k["id"] for k in needed_before if "id" in k)
|
|
95
|
+
.exists()
|
|
96
|
+
.ids
|
|
97
|
+
)
|
|
98
|
+
needed_before = {
|
|
99
|
+
k: v
|
|
100
|
+
for k, v in needed_before.items()
|
|
101
|
+
if "id" not in k or k["id"] in line_ids
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# old key to new key for the same line
|
|
105
|
+
before2after = {
|
|
106
|
+
before: inv_existing_after[bline]
|
|
107
|
+
for bline, before in inv_existing_before.items()
|
|
108
|
+
if bline in inv_existing_after
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if needed_after == needed_before:
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
existing_after = defaultdict(list)
|
|
115
|
+
for k, v in inv_existing_after.items():
|
|
116
|
+
existing_after[v].append(k)
|
|
117
|
+
to_delete = [
|
|
118
|
+
line.id
|
|
119
|
+
for line, key in inv_existing_before.items()
|
|
120
|
+
if key not in needed_after
|
|
121
|
+
and key in existing_after
|
|
122
|
+
and before2after[key] not in needed_after
|
|
123
|
+
]
|
|
124
|
+
to_delete_set = set(to_delete)
|
|
125
|
+
to_delete.extend(
|
|
126
|
+
line.id
|
|
127
|
+
for line, key in inv_existing_after.items()
|
|
128
|
+
if key not in needed_after and line.id not in to_delete_set
|
|
129
|
+
)
|
|
130
|
+
to_create = {
|
|
131
|
+
key: values
|
|
132
|
+
for key, values in needed_after.items()
|
|
133
|
+
if key not in existing_after
|
|
134
|
+
}
|
|
135
|
+
to_write = {
|
|
136
|
+
line: values
|
|
137
|
+
for key, values in needed_after.items()
|
|
138
|
+
for line in existing_after[key]
|
|
139
|
+
if any(
|
|
140
|
+
self.env["account.move.line"]
|
|
141
|
+
._fields[fname]
|
|
142
|
+
.convert_to_write(line[fname], self)
|
|
143
|
+
!= values[fname]
|
|
144
|
+
for fname in values
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
while to_delete and to_create:
|
|
149
|
+
key, values = to_create.popitem()
|
|
150
|
+
line_id = to_delete.pop()
|
|
151
|
+
self.env["account.move.line"].browse(line_id).write(
|
|
152
|
+
{**key, **values, "display_type": line_type}
|
|
153
|
+
)
|
|
154
|
+
if to_delete:
|
|
155
|
+
self.env["account.move.line"].browse(to_delete).with_context(
|
|
156
|
+
dynamic_unlink=True
|
|
157
|
+
).unlink()
|
|
158
|
+
if to_create:
|
|
159
|
+
self.env["account.move.line"].create(
|
|
160
|
+
[
|
|
161
|
+
{**key, **values, "display_type": line_type}
|
|
162
|
+
for key, values in to_create.items()
|
|
163
|
+
]
|
|
164
|
+
)
|
|
165
|
+
if to_write:
|
|
166
|
+
for line, values in to_write.items():
|
|
167
|
+
line.write(values)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from odoo import models, fields, api, _
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PayrollCustomConcept(models.Model):
|
|
7
|
+
_name = "payroll.custom.concept"
|
|
8
|
+
_description = "Payroll Custom Concept"
|
|
9
|
+
_check_company_auto = True
|
|
10
|
+
|
|
11
|
+
@api.model
|
|
12
|
+
def create(self, vals):
|
|
13
|
+
default_account_ext_id = "l10n_es.%s_%s" % (
|
|
14
|
+
self.env.company.id, vals.get("default_account_xml_id", False)
|
|
15
|
+
)
|
|
16
|
+
if default_account_ext_id:
|
|
17
|
+
account = self.env.ref(default_account_ext_id, False)
|
|
18
|
+
vals.update({"account_id": account.id if account else False})
|
|
19
|
+
|
|
20
|
+
return super(PayrollCustomConcept, self).create(vals)
|
|
21
|
+
|
|
22
|
+
def _default_account(self):
|
|
23
|
+
default_account_ext_id = "l10n_es.%s_%s" % (
|
|
24
|
+
self.env.company.id, self.default_account_xml_id
|
|
25
|
+
)
|
|
26
|
+
default_account = self.env.ref(default_account_ext_id, False)
|
|
27
|
+
return default_account.id if default_account else False
|
|
28
|
+
|
|
29
|
+
name = fields.Char(
|
|
30
|
+
string="Tag",
|
|
31
|
+
required=True,
|
|
32
|
+
help=_("Indicate the name of the custom concept.")
|
|
33
|
+
)
|
|
34
|
+
col_index = fields.Integer(
|
|
35
|
+
string="Column Number",
|
|
36
|
+
required=True,
|
|
37
|
+
help=_("Indicate the position in the file.")
|
|
38
|
+
)
|
|
39
|
+
account_id = fields.Many2one(
|
|
40
|
+
comodel_name="account.account",
|
|
41
|
+
string="Account",
|
|
42
|
+
required=True,
|
|
43
|
+
help=_("Indicate the account to post the data.")
|
|
44
|
+
)
|
|
45
|
+
default_account_xml_id = fields.Char("Default Account XML ID")
|
|
46
|
+
payroll_import_setup_id = fields.Many2one(
|
|
47
|
+
comodel_name="payroll.import.setup",
|
|
48
|
+
string="Payroll Import Setup",
|
|
49
|
+
required=True,
|
|
50
|
+
ondelete="cascade",
|
|
51
|
+
)
|
|
52
|
+
company_id = fields.Many2one(
|
|
53
|
+
related="payroll_import_setup_id.company_id",
|
|
54
|
+
string="Company",
|
|
55
|
+
store=True,
|
|
56
|
+
readonly=True,
|
|
57
|
+
)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from odoo import models, fields, _
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PayrollImportMapping(models.Model):
|
|
7
|
+
_name = "payroll.import.mapping"
|
|
8
|
+
_description = "Payroll Import Mapping"
|
|
9
|
+
|
|
10
|
+
name = fields.Char(string="Name", required=True, default="payroll.import.setup")
|
|
11
|
+
res_model = fields.Char(string=_("Model"))
|
|
12
|
+
res_field = fields.Char(string=_("Field"))
|
|
13
|
+
column_field = fields.Char(string=_("Column Field"), required=True)
|
|
14
|
+
account_field = fields.Char(string=_("Account Field"))
|
|
15
|
+
tax_field = fields.Char(string=_("Tax Field"))
|
|
16
|
+
aggregate_field = fields.Char(string=_("Aggregate Flag Field"))
|
|
17
|
+
payroll_import_setup_id = fields.Many2many(
|
|
18
|
+
comodel_name="payroll.import.setup",
|
|
19
|
+
string="Payroll Import Setup",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
_sql_constraints = [
|
|
23
|
+
(
|
|
24
|
+
"unique_mapping",
|
|
25
|
+
"unique(name, res_model, res_field, column_field)",
|
|
26
|
+
"This mapping already exists.",
|
|
27
|
+
)
|
|
28
|
+
]
|