Merge PR #616 into 16.0

Signed-off-by pedrobaeza
This commit is contained in:
OCA-git-bot
2024-06-03 08:58:45 +00:00
22 changed files with 1022 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
===========================
Online Bank Statements: OFX
===========================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:e6ebad00d6c39c92584c6ee76b5deae328742e8a81229971ce4ca37d2f1e4cb1
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fbank--statement--import-lightgray.png?logo=github
:target: https://github.com/OCA/bank-statement-import/tree/16.0/account_statement_import_online_ofx
:alt: OCA/bank-statement-import
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/bank-statement-import-16-0/bank-statement-import-16-0-account_statement_import_online_ofx
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/bank-statement-import&target_branch=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This module provides online bank statements from Open Financial Exchange (OFX) institutions.
You can set-up your own provider, or import a list of supported providers.
https://ofxhome.com/ is used as a data source, currently over 300 institutions are supported.
**Table of contents**
.. contents::
:local:
Configuration
=============
To configure online bank statements provider:
#. Go to *Invoicing > Configuration > Online Bank Statement Providers*
#. Create a provider and configure provider-specific settings.
If you want to allow empty bank statements to be created every time the
information is pulled, you can check the option "Allow empty statements"
at the provider configuration level.
**NOTE**: To access these features, user needs to belong to
*Show Full Accounting Features* group.
Usage
=====
To pull historical bank statements:
#. Go to *Invoicing > Configuration > Journals*
#. Select specific bank accounts
#. Launch *Actions > Online Bank Statements Pull Wizard*
#. Configure date interval and click *Pull*
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/bank-statement-import/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/bank-statement-import/issues/new?body=module:%20account_statement_import_online_ofx%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* ForgeFlow
Contributors
~~~~~~~~~~~~
* `ForgeFlow <https://www.forgeflow.com/>`__
* Jasmin Solanki <jasmin.solanki@forgeflow.com>
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
This module is part of the `OCA/bank-statement-import <https://github.com/OCA/bank-statement-import/tree/16.0/account_statement_import_online_ofx>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -0,0 +1,4 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import models
from . import wizards

View File

@@ -0,0 +1,22 @@
# Copyright 2023 ForgeFlow S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Online Bank Statements: OFX",
"version": "16.0.1.0.0",
"author": "ForgeFlow, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/bank-statement-import",
"license": "AGPL-3",
"category": "Accounting",
"summary": "Online bank statements for OFX",
"depends": [
"account_statement_import_online",
],
"external_dependencies": {"python": ["ofxtools", "ofxparse"]},
"data": [
"security/ir.model.access.csv",
"views/online_bank_statement_provider.xml",
"wizards/online_bank_statement_pull_wizard.xml",
],
"installable": True,
}

View File

@@ -0,0 +1,4 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import ofx_institution
from . import online_bank_statement_provider_ofx

View File

@@ -0,0 +1,14 @@
# Copyright 2023 ForgeFlow S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
class OFXInstitution(models.Model):
_name = "ofx.institution"
_description = "OFX Institution"
name = fields.Char()
nickname = fields.Char()
ofxhome_id = fields.Integer()

View File

@@ -0,0 +1,187 @@
# Copyright 2023 ForgeFlow S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
import io
import time
import xml.etree.ElementTree as ET
from datetime import datetime
import requests
from ofxparse import OfxParser
from ofxtools import ofxhome, utils
from ofxtools.Client import OFXClient, StmtRq
from odoo import _, api, fields, models
from odoo.exceptions import UserError
class OnlineBankStatementProviderOFX(models.Model):
_inherit = "online.bank.statement.provider"
ofx_institution_line_ids = fields.One2many("ofx.institution.line", "provider_id")
@api.model
def _get_available_services(self):
return super()._get_available_services() + [
("OFX", "OFX"),
]
def get_statements(self, client, password, s1, try_no=1):
statements = client.request_statements(password, s1, skip_profile=True)
file_data = statements.read()
if b"Request unsuccessful. Incapsula incident ID" in file_data and try_no <= 3:
time.sleep(3)
return self.get_statements(client, password, s1, try_no + 1)
ofx = OfxParser.parse(io.BytesIO(file_data))
if ofx.status.get("code") != 0:
raise UserError(file_data)
return ofx
def _obtain_statement_data(self, date_since, date_until):
self.ensure_one()
is_scheduled = self.env.context.get("scheduled")
# set date_until as today's date if its greter than today's date
# to avoid failure of request.
today = datetime.now()
today = today.replace(hour=0, minute=0, second=0, microsecond=0)
if date_until > today:
date_until = today
if self.service != "OFX":
return super()._obtain_statement_data(
date_since,
date_until,
) # pragma: no cover
lines = []
if is_scheduled:
ofx_institution_lines = self.ofx_institution_line_ids
else:
ofx_institution_lines = self.env["ofx.institution.line"].browse(
self._context.get("ofx_institution_ids")
)
for ofx_institution_line in ofx_institution_lines:
try:
username = ofx_institution_line.username
password = ofx_institution_line.password
bankid = ofx_institution_line.bankid
ofxhome_id = ofx_institution_line.institution_id.ofxhome_id
acctid = ofx_institution_line.account_id
institute = ofxhome.lookup(ofxhome_id)
if institute is None or institute.url is None:
raise UserError(_("OFX Data is not available"))
ofxhome_id = institute.id
client = OFXClient(
institute.url,
userid=username,
org=institute.org,
fid=institute.fid,
bankid=bankid,
)
dtstart = date_since.replace(tzinfo=utils.UTC)
dtend = date_until.replace(tzinfo=utils.UTC)
s1 = StmtRq(
acctid=acctid,
dtstart=dtstart,
dtend=dtend,
accttype="CHECKING",
)
ofx = self.get_statements(client, password, s1)
for account in ofx.accounts:
if not account.statement.transactions:
continue
for transaction in account.statement.transactions:
vals = self._prepare_ofx_transaction_line(transaction)
if vals:
lines.append(vals)
except Exception as e:
raise UserError(
_("The following problem occurred during import.\n\n %s") % str(e)
) from e
return lines, {}
@api.model
def _prepare_ofx_transaction_line(self, transaction):
payment_ref = transaction.payee
if transaction.checknum:
payment_ref += " " + transaction.checknum
if transaction.memo:
payment_ref += " : " + transaction.memo
vals = {
"date": transaction.date,
"payment_ref": payment_ref,
"amount": float(transaction.amount),
"unique_import_id": transaction.id,
}
return vals
def import_ofx_institutions(self):
OfxInstitution = self.env["ofx.institution"]
try:
with requests.get(
"http://www.ofxhome.com/api.php?all=yes", timeout=30
) as f:
response = f.text
institute_list = {
fi.get("id").strip(): fi.get("name").strip()
for fi in ET.fromstring(response)
}
except Exception as e:
raise UserError(_(e)) from e
for ofxhome_id, name in institute_list.items():
institute = OfxInstitution.search([("ofxhome_id", "=", ofxhome_id)])
vals = {
"name": name,
"ofxhome_id": ofxhome_id,
}
if institute:
institute.write(vals)
else:
OfxInstitution.create(vals)
def _create_or_update_statement(
self, data, statement_date_since, statement_date_until
):
# deleting blank statement for OFX online import
res = super()._create_or_update_statement(
data, statement_date_since, statement_date_until
)
if self.service == "OFX":
unfiltered_lines, statement_values = data
if not unfiltered_lines:
unfiltered_lines = []
if not statement_values:
statement_values = {}
statement_values["name"] = self.make_statement_name(statement_date_since)
filtered_lines = self._get_statement_filtered_lines(
unfiltered_lines,
statement_values,
statement_date_since,
statement_date_until,
)
if not filtered_lines:
return
if filtered_lines:
statement_values.update(
{"line_ids": [[0, False, line] for line in filtered_lines]}
)
self._update_statement_balances(statement_values)
statement = self._statement_create_or_write(statement_values)
self._journal_set_statement_source()
if not statement.line_ids:
statement.unlink()
return res
class OFXInstitutionLine(models.Model):
_name = "ofx.institution.line"
_description = "OFX Institution Line"
_rec_name = "institution_id"
institution_id = fields.Many2one("ofx.institution", "Institution")
username = fields.Char()
password = fields.Char()
bankid = fields.Char()
provider_id = fields.Many2one("online.bank.statement.provider")
account_id = fields.Char()

View File

@@ -0,0 +1,11 @@
To configure online bank statements provider:
#. Go to *Invoicing > Configuration > Online Bank Statement Providers*
#. Create a provider and configure provider-specific settings.
If you want to allow empty bank statements to be created every time the
information is pulled, you can check the option "Allow empty statements"
at the provider configuration level.
**NOTE**: To access these features, user needs to belong to
*Show Full Accounting Features* group.

View File

@@ -0,0 +1,3 @@
* `ForgeFlow <https://www.forgeflow.com/>`__
* Jasmin Solanki <jasmin.solanki@forgeflow.com>

View File

@@ -0,0 +1,3 @@
This module provides online bank statements from Open Financial Exchange (OFX) institutions.
You can set-up your own provider, or import a list of supported providers.
https://ofxhome.com/ is used as a data source, currently over 300 institutions are supported.

View File

@@ -0,0 +1,6 @@
To pull historical bank statements:
#. Go to *Invoicing > Configuration > Journals*
#. Select specific bank accounts
#. Launch *Actions > Online Bank Statements Pull Wizard*
#. Configure date interval and click *Pull*

View File

@@ -0,0 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_ofx_institution_manager,ofx_institution,model_ofx_institution,account.group_account_user,1,1,1,1
access_ofx_institution_user,ofx_institution,model_ofx_institution,base.group_user,1,0,0,0
access_ofx_institution_line_user,access_ofx_institution_line,model_ofx_institution_line,base.group_user,1,0,0,0
access_ofx_institution_line_manager,access_ofx_institution_line,model_ofx_institution_line,account.group_account_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_ofx_institution_manager ofx_institution model_ofx_institution account.group_account_user 1 1 1 1
3 access_ofx_institution_user ofx_institution model_ofx_institution base.group_user 1 0 0 0
4 access_ofx_institution_line_user access_ofx_institution_line model_ofx_institution_line base.group_user 1 0 0 0
5 access_ofx_institution_line_manager access_ofx_institution_line model_ofx_institution_line account.group_account_user 1 1 1 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,451 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>Online Bank Statements: OFX</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="online-bank-statements-ofx">
<h1 class="title">Online Bank Statements: OFX</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:e6ebad00d6c39c92584c6ee76b5deae328742e8a81229971ce4ca37d2f1e4cb1
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/bank-statement-import/tree/16.0/account_statement_import_online_ofx"><img alt="OCA/bank-statement-import" src="https://img.shields.io/badge/github-OCA%2Fbank--statement--import-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/bank-statement-import-16-0/bank-statement-import-16-0-account_statement_import_online_ofx"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/bank-statement-import&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module provides online bank statements from Open Financial Exchange (OFX) institutions.
You can set-up your own provider, or import a list of supported providers.
<a class="reference external" href="https://ofxhome.com/">https://ofxhome.com/</a> is used as a data source, currently over 300 institutions are supported.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a></li>
<li><a class="reference internal" href="#usage" id="toc-entry-2">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#toc-entry-1">Configuration</a></h1>
<p>To configure online bank statements provider:</p>
<ol class="arabic simple">
<li>Go to <em>Invoicing &gt; Configuration &gt; Online Bank Statement Providers</em></li>
<li>Create a provider and configure provider-specific settings.</li>
</ol>
<p>If you want to allow empty bank statements to be created every time the
information is pulled, you can check the option “Allow empty statements”
at the provider configuration level.</p>
<p><strong>NOTE</strong>: To access these features, user needs to belong to
<em>Show Full Accounting Features</em> group.</p>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
<p>To pull historical bank statements:</p>
<ol class="arabic simple">
<li>Go to <em>Invoicing &gt; Configuration &gt; Journals</em></li>
<li>Select specific bank accounts</li>
<li>Launch <em>Actions &gt; Online Bank Statements Pull Wizard</em></li>
<li>Configure date interval and click <em>Pull</em></li>
</ol>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/bank-statement-import/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/bank-statement-import/issues/new?body=module:%20account_statement_import_online_ofx%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#toc-entry-4">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-5">Authors</a></h2>
<ul class="simple">
<li>ForgeFlow</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-6">Contributors</a></h2>
<ul class="simple">
<li><a class="reference external" href="https://www.forgeflow.com/">ForgeFlow</a><ul>
<li>Jasmin Solanki &lt;<a class="reference external" href="mailto:jasmin.solanki&#64;forgeflow.com">jasmin.solanki&#64;forgeflow.com</a>&gt;</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-7">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/bank-statement-import/tree/16.0/account_statement_import_online_ofx">OCA/bank-statement-import</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,3 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import test_account_statement_import_online_ofx

View File

@@ -0,0 +1,95 @@
# Copyright 2023 ForgeFlow S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from datetime import datetime
from unittest import mock
from dateutil.relativedelta import relativedelta
from odoo import fields
from odoo.tests import common
_module_ns = "odoo.addons.account_statement_import_online_ofx"
_provider_class = (
_module_ns
+ ".models.online_bank_statement_provider_ofx"
+ ".OnlineBankStatementProviderOFX"
)
class TestAccountBankAccountStatementImportOnlineOFX(common.TransactionCase):
def setUp(self):
super().setUp()
self.now = fields.Datetime.now()
self.today = datetime(self.now.year, self.now.month, self.now.day)
self.yesterday = self.today - relativedelta(days=1)
self.AccountJournal = self.env["account.journal"]
self.OnlineBankStatementProvider = self.env["online.bank.statement.provider"]
self.AccountBankStatement = self.env["account.bank.statement"]
self.AccountBankStatementLine = self.env["account.bank.statement.line"]
self.OfxInstitutionLine = self.env["ofx.institution.line"]
self.ofx_institute = self.env["ofx.institution"].create(
{"name": "Test", "nickname": "Test", "ofxhome_id": 1}
)
def test_import_online_ofx(self):
provider_model = self.env["online.bank.statement.provider"]
active_id = self.env.context.get("active_id")
provider = provider_model.browse(active_id)
# Create OFX institution line in OFX provider
self.OfxInstitutionLine.create(
{
"institution_id": self.ofx_institute.id,
"username": "Test",
"password": "Test",
"bankid": "1234",
"provider_id": provider.id,
"account_id": "1234",
}
)
# import statement
mocked_response = [
{
"date": self.today,
"payment_ref": "BANKCARD 12345678",
"amount": 5645.07,
"unique_import_id": "202302211",
},
{
"date": self.today,
"payment_ref": "ELECTRONIC IMAGE DEPOSIT",
"amount": 2874.91,
"unique_import_id": "202302212",
},
{
"date": self.today,
"payment_ref": "BANKCARD 567890123",
"amount": 1269.18,
"unique_import_id": "202302213",
},
], {}
with mock.patch(
_provider_class + "._obtain_statement_data",
return_value=mocked_response,
):
data = provider._obtain_statement_data(
self.yesterday,
self.today,
)
self.assertEqual(len(data[0]), 3)
self.assertEqual(
data[0][1],
{
"date": self.today,
"payment_ref": "ELECTRONIC IMAGE DEPOSIT",
"amount": 2874.91,
"unique_import_id": "202302212",
},
)
self.assertEqual(data[1], {})

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="online_bank_statement_provider_form">
<field name="name">online.bank.statement.provider.form</field>
<field name="model">online.bank.statement.provider</field>
<field
name="inherit_id"
ref="account_statement_import_online.online_bank_statement_provider_form"
/>
<field name="arch" type="xml">
<xpath expr="//sheet" position="before">
<header>
<button
name="import_ofx_institutions"
type="object"
string="Import OFX Institutions"
attrs="{'invisible': [('service', '!=', 'OFX')]}"
/>
</header>
</xpath>
<xpath expr="//sheet" position="inside">
<notebook>
<page
name="ofx_institutions_list"
string="OFX Institutions"
attrs="{'invisible': [('service', '!=', 'OFX')]}"
>
<field name="ofx_institution_line_ids" mode="tree">
<tree editable="top">
<field
name="institution_id"
options="{'no_create_edit': True, 'no_quick_create': True}"
/>
<field name="username" required="1" />
<field name="password" password="1" required="1" />
<field name="bankid" required="1" />
<field name="account_id" required="1" />
</tree>
</field>
</page>
</notebook>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,3 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import online_bank_statement_pull_wizard

View File

@@ -0,0 +1,31 @@
# Copyright 2023 ForgeFlow S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models
class OnlineBankStatementPullWizard(models.TransientModel):
_inherit = "online.bank.statement.pull.wizard"
ofx_institution_ids = fields.Many2many(
string="OFX Institutions",
comodel_name="ofx.institution.line",
column1="wizard_id",
column2="institution_line_id",
relation="ofx_institution_line_pull_wizard_rel",
)
is_ofx_provider = fields.Boolean()
@api.onchange("date_since")
def _compute_is_ofx_provider(self):
provider_model = self.env["online.bank.statement.provider"]
active_id = self.env.context.get("active_id")
provider = provider_model.browse(active_id)
self.is_ofx_provider = provider.service == "OFX"
def action_pull(self):
return super(
OnlineBankStatementPullWizard,
self.with_context(ofx_institution_ids=self.ofx_institution_ids.ids),
).action_pull()

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record model="ir.ui.view" id="online_bank_statement_pull_wizard_form">
<field name="name">online.bank.statement.pull.wizard.form</field>
<field name="model">online.bank.statement.pull.wizard</field>
<field
name="inherit_id"
ref="account_statement_import_online.online_bank_statement_pull_wizard_form"
/>
<field name="arch" type="xml">
<field name="date_since" position="before">
<field name="is_ofx_provider" invisible="1" />
<field
name="ofx_institution_ids"
options="{'no_create_edit': True, 'no_quick_create': True}"
widget="many2many_tags"
attrs="{'invisible':[('is_ofx_provider','!=',True)],'required':[('is_ofx_provider','=',True)]}"
/>
</field>
</field>
</record>
</odoo>

View File

@@ -1,4 +1,5 @@
# generated from manifests external_dependencies
chardet
ofxparse
ofxtools
xlrd

View File

@@ -0,0 +1 @@
../../../../account_statement_import_online_ofx

View File

@@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)