Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ class AccountStatementImportApi(models.Model):
string="Password or Client Secret", groups="base.group_system"
)
last_success = fields.Datetime(compute="_compute_last_success")
backward_days = fields.Integer() # API-specific module should show/hide it
show_backward_days = fields.Boolean(compute="_compute_show")
cron_id = fields.Many2one("ir.cron", string="Scheduled Action", readonly=True)
log_ids = fields.One2many(
"account.statement.import.api.log",
Expand Down Expand Up @@ -100,11 +98,6 @@ class AccountStatementImportApi(models.Model):
"unique(name, company_id)",
"A bank statement import API already exists with that name is this company.",
),
(
"backward_days_positive",
"CHECK(backward_days >= 0)",
"Backward days must be positive or null.",
),
]

@api.model
Expand Down Expand Up @@ -160,7 +153,6 @@ def _compute_last_success(self):
def _compute_show(self):
service2info = self._get_service_info()
for rec in self:
show_backward_days = True
show_company_user = True
instructions = False
show_add_account_wizard = False
Expand All @@ -170,7 +162,6 @@ def _compute_show(self):
show_password = False
if rec.service:
info = service2info[rec.service]
show_backward_days = info.get("show_backward_days", True)
show_company_user = info.get("user_company_required", True)
instructions = info.get("instructions")
show_add_account_wizard = info.get(
Expand All @@ -180,7 +171,6 @@ def _compute_show(self):
is_aggregator = info.get("is_aggregator")
show_login = info.get("login") == "field"
show_password = info.get("password") == "field"
rec.show_backward_days = show_backward_days
rec.show_company_user = show_company_user
rec.instructions = instructions
rec.show_add_account_wizard = show_add_account_wizard
Expand Down Expand Up @@ -252,7 +242,6 @@ def _prepare_speedy(self):
"tz": self.tz and pytz.timezone(self.tz) or pytz.utc,
"service": self.service,
"service_info": self._get_service_info()[self.service],
"backward_days": self.backward_days,
"login": self.sudo().login,
"password": self.sudo().password,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,8 @@
password="1"
attrs="{'invisible': [('show_password', '=', False)], 'required': [('show_password', '=', True)]}"
/>
<field
name="backward_days"
attrs="{'invisible': [('show_backward_days', '=', False)]}"
/>
<field name="show_login" invisible="1" />
<field name="show_password" invisible="1" />
<field name="show_backward_days" invisible="1" />
<field name="show_company_user" invisible="1" />
<field name="tz" />
</group>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@ def _api_import_bridge(self, result, speedy):
since_iso = f"{since_iso[:-6]}Z"
params["since"] = since_iso
else:
params["min_date"] = self.statement_import_api_start_date - timedelta(
import_api.backward_days
)
params["min_date"] = self.statement_import_api_start_date

transactions = import_api._bridge_get_all_pages(
"aggregation/transactions", headers, result, speedy, params
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ def _get_service_info(self):
"login": "config_file",
"password": "config_file",
"user_company_required": True,
"show_backward_days": True,
"manage_accounts_wizard": True,
"manage_accounts_wizard_connector_required": True,
"is_aggregator": True,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,9 @@ def _api_import_powens(self, result, speedy):
since_iso = f"{since_iso[:-6]}Z"
params = {"last_update": since_iso}
else:
min_date = self.statement_import_api_start_date - timedelta(
import_api.backward_days
)
params = {
"filter": "date", # by default, it filters on "application_date"
"min_date": min_date,
"min_date": self.statement_import_api_start_date,
}
user_id = speedy["company_id2user_identifier"][company.id]
api_name = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ def _get_service_info(self):
"login": "config_file",
"password": "config_file",
"user_company_required": True,
"show_backward_days": True,
"manage_accounts_wizard": True,
"is_aggregator": True,
# "instructions": _("TODO"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ def _api_import_qonto_prepare_pivot_line(self, trans, result, speedy):
"in_invoice_force_invoice_date": self._api_import_timestamp_iso8601_to_date(
trans["emitted_at"][:10], speedy
),
"in_invoice_analytic_account_idents": trans["label_ids"],
}
if trans["reference"]:
pivot["payment_ref"] = " ".join([pivot["payment_ref"], trans["reference"]])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def _get_service_info(self):
"login": "field",
"password": "field",
"user_company_required": False,
"show_backward_days": False,
"show_analytic_button": True,
"instructions": _(
"<p>Go to the web interface of your "
'<a href="https://qonto.com/">Qonto</a> account. '
Expand Down Expand Up @@ -75,6 +75,25 @@ def _update_api_accounts_qonto(self, company, result, speedy):
}
return account_ident2vals

def _update_api_analytic_accounts_qonto(self, company, result, speedy):
self.ensure_one()
accounts = self._qonto_get_all_pages("labels", result, speedy)
parent_accounts = {}
child_accounts = []
for account in accounts:
if account.get("parent_id"):
child_accounts.append(account)
else:
parent_accounts[account["id"]] = account["name"]
account_ident2vals = {}
for account in child_accounts:
account_ident2vals[str(account["id"])] = {
"name": account["name"],
"parent_name": parent_accounts.get(account.get("parent_id")),
"company_id": company.id,
}
return account_ident2vals

@api.model
def _qonto_get_all_pages(self, api_name, result, speedy, params=None):
ajo = self.env["account.journal"]
Expand Down
8 changes: 7 additions & 1 deletion account_statement_import_in_invoice_api/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@
"maintainers": ["alexis-via"],
"website": "https://github.com/akretion/bank-statement-import-api",
"depends": ["account_statement_import_api", "account_statement_import_in_invoice"],
"data": [],
"external_dependencies": {"python": ["unidecode"]},
"data": [
"security/ir.model.access.csv",
"views/account_bank_statement_analytic_account.xml",
"views/account_bank_statement_line.xml",
"views/account_statement_import_api.xml",
],
"installable": True,
"auto_install": True,
}
2 changes: 2 additions & 0 deletions account_statement_import_in_invoice_api/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
from . import account_bank_statement_analytic_account
from . import account_bank_statement_line
from . import account_statement_import_api
from . import account_journal
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright 2026 Akretion France (https://www.akretion.com/)
# @author: Benoit Guillot <benoit.guillot@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import fields, models


class AccountBankStatementAnalyticAccount(models.Model):
_name = "account.bank.statement.analytic.account"
_description = "Bank Statement Analytic Account"
_order = "service, parent_name, name"

statement_import_api_id = fields.Many2one(
"account.statement.import.api",
ondelete="cascade",
string="Statement Import API",
required=True,
)
identifier = fields.Char(
required=True,
readonly=True,
help="Technical ID given by the bank statement import provider for this "
"analytic account.",
)
name = fields.Char(required=True, string="Label")
service = fields.Selection(related="statement_import_api_id.service", store=True)
parent_name = fields.Char(required=True, index=True, string="Parent Label")
analytic_account_id = fields.Many2one(
"account.analytic.account",
domain="[('company_id', 'in', [False, company_id])]",
)
active = fields.Boolean(default=True)
company_id = fields.Many2one(
comodel_name="res.company", required=True, readonly=True
)

_sql_constraints = [
(
"identifier_service_company_uniq",
"unique(identifier, service, company)",
"This identifier already exists for this service and this company.",
)
]

def name_get(self):
res = []
for rec in self:
name = rec.name
if rec.parent_name:
name = f"[{rec.parent_name}] {name}"
res.append((rec.id, name))
return res
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Copyright 2025 Akretion France (https://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).


from odoo import _, api, fields, models


class AccountBankStatementLine(models.Model):
_inherit = "account.bank.statement.line"

in_invoice_bank_statement_analytic_account_ids = fields.Many2many(
comodel_name="account.bank.statement.analytic.account",
relation="in_invoice_analytic_account_rel",
string="Analytic Accounts",
)
warn_message = fields.Char(compute="_compute_warning_message")

@api.depends(
"partner_id",
"in_invoice_account_id",
"in_invoice_bank_statement_analytic_account_ids",
"in_invoice_bank_statement_analytic_account_ids.analytic_account_id",
)
def _compute_in_invoice_analytic_distribution(self):
default_lines = self
for line in self:
distribution = {}
for (
analytic_account
) in line.in_invoice_bank_statement_analytic_account_ids.filtered(
"analytic_account_id"
):
distribution[str(analytic_account.analytic_account_id.id)] = 100
line.in_invoice_analytic_distribution = distribution
if distribution:
default_lines -= line
if default_lines:
return super(
AccountBankStatementLine, default_lines
)._compute_in_invoice_analytic_distribution()

@api.depends(
"in_invoice_bank_statement_analytic_account_ids",
"in_invoice_bank_statement_analytic_account_ids.analytic_account_id",
)
def _compute_warning_message(self):
for line in self:
message = False
missing_analytic = (
line.in_invoice_bank_statement_analytic_account_ids.filtered(
lambda x: not x.analytic_account_id
)
)
if missing_analytic:
message = _(
"The following bank statement analytic account(s) are not "
"mapped to an analytic account: %s",
", ".join(missing_analytic.mapped("display_name")),
)
line.warn_message = message
36 changes: 36 additions & 0 deletions account_statement_import_in_invoice_api/models/account_journal.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,42 @@ def _api_import_prepare_bank_statement_line(
f"Expense category code '{expcateg_code}' doesn't exist "
f"for service {speedy['service']}",
)
bs_analytic_account_idents = pivot_line.get(
"in_invoice_analytic_account_idents"
)
bs_analytic_account_ids = []
company_id = self.company_id.id
bs_ana_account_company_ident2vals = speedy[
"bs_analytic_account_company_ident2vals"
]
for bs_analytic_account_ident in bs_analytic_account_idents:
company_ident_key = (company_id, bs_analytic_account_ident)
if company_ident_key in bs_ana_account_company_ident2vals:
bs_analytic_account_ids.append(
bs_ana_account_company_ident2vals[company_ident_key]["id"]
)
if not bs_ana_account_company_ident2vals[company_ident_key][
"analytic_account_id"
]:
analytic_account_dname = bs_ana_account_company_ident2vals[
company_ident_key
]["display_name"]
self._api_import_warning_log(
result,
f"Analytic account '{analytic_account_dname}' is not "
f"mapped for service {speedy['service']}",
)
else:
self._api_import_warning_log(
result,
f"Analytic account identifier '{bs_analytic_account_ident}' doesn't "
f"exist in company {self.company_id.display_name} "
f"for service {speedy['service']}",
)

lvals["in_invoice_bank_statement_analytic_account_ids"] = [
Command.set(bs_analytic_account_ids)
]
# for the moment, we consider that autoliq taxes are set by country-specific modules
# that inherit this method
in_invoice_tax_ids = []
Expand Down
Loading
Loading