Skip to content

Commit

Permalink
Fix conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
feketemihai committed Mar 11, 2024
1 parent 85f33af commit 0df3866
Show file tree
Hide file tree
Showing 24 changed files with 283 additions and 128 deletions.
2 changes: 1 addition & 1 deletion l10n_ro_account_anaf_sync/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Romania - Account ANAF Sync
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:fd3e4bff53bd0db2cd49ee906caa77ecc3b2f6aae1c62fac37ae91e3193e870e
!! source digest: sha256:5101958b635714560aaac5b75a11aa44a5acaf7311a31f5dff0c5472d4a4b3e6
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Mature-brightgreen.png
Expand Down
1 change: 1 addition & 0 deletions l10n_ro_account_anaf_sync/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@
"installable": True,
"development_status": "Mature",
"maintainers": ["feketemihai"],
"external_dependencies": {"python": ["PyJWT"]},
}
35 changes: 25 additions & 10 deletions l10n_ro_account_anaf_sync/controllers/anaf_oauth.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
# Copyright (C) 2022 NextERP Romania
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

# Authorization Endpoint https://logincert.anaf.ro/anaf-oauth2/v1/authorize
# Token Issuance Endpoint https://logincert.anaf.ro/anaf-oauth2/v1/token
# Token Revocation Endpoint https://logincert.anaf.ro/anaf-oauth2/v1/revoke
import logging
import secrets
from datetime import datetime, timedelta

import jwt
import requests

from odoo import _, http
from odoo.http import request

# Authorization Endpoint https://logincert.anaf.ro/anaf-oauth2/v1/authorize
# Token Issuance Endpoint https://logincert.anaf.ro/anaf-oauth2/v1/token
# Token Revocation Endpoint https://logincert.anaf.ro/anaf-oauth2/v1/revoke
_logger = logging.getLogger(__name__)


class AccountANAFSyncWeb(http.Controller):
Expand Down Expand Up @@ -62,10 +65,13 @@ def redirect_anaf(self, anaf_config_id, **kw):
client_id = anaf_config.client_id
url = user.get_base_url()
odoo_oauth_url = f"{url}/l10n_ro_account_anaf_sync/anaf_oauth/{anaf_config.id}"
redirect_url = "%s?response_type=code&client_id=%s&redirect_uri=%s" % (
anaf_config.anaf_oauth_url + "/authorize",
client_id,
odoo_oauth_url,
redirect_url = (
"%s?response_type=code&client_id=%s&redirect_uri=%s&token_content_type=jwt"
% (
anaf_config.anaf_oauth_url + "/authorize",
client_id,
odoo_oauth_url,
)
)
anaf_request_from_redirect = request.redirect(
redirect_url, code=302, local=False
Expand Down Expand Up @@ -94,7 +100,7 @@ def get_anaf_oauth_code(self, anaf_config_id, **kw):
"Returns a text with the result of anaf request from redirect"
uid = request.uid
user = request.env["res.users"].browse(uid)
now = datetime.now()
datetime.now()
ANAF_Configs = request.env["l10n.ro.account.anaf.sync"].sudo()
anaf_config = ANAF_Configs.browse(anaf_config_id)

Expand Down Expand Up @@ -126,6 +132,7 @@ def get_anaf_oauth_code(self, anaf_config_id, **kw):
"code": "{}".format(code),
"access_key": "{}".format(code),
"redirect_uri": "{}".format(redirect_uri),
"token_content_type": "jwt",
}
response = requests.post(
anaf_config.anaf_oauth_url + "/token",
Expand All @@ -134,12 +141,20 @@ def get_anaf_oauth_code(self, anaf_config_id, **kw):
timeout=1.5,
)
response_json = response.json()

acces_token = {}
if response_json.get("access_token", None):
acces_token = jwt.decode(

Check warning on line 146 in l10n_ro_account_anaf_sync/controllers/anaf_oauth.py

View check run for this annotation

Codecov / codecov/patch

l10n_ro_account_anaf_sync/controllers/anaf_oauth.py#L146

Added line #L146 was not covered by tests
response_json.get("access_token"),
algorithms=["RS512"],
options={"verify_signature": False},
)
message = _("The response was finished.\nResponse was: %s") % response_json
anaf_config.write(
{
"code": code,
"client_token_valability": now + timedelta(days=89),
"client_token_valability": datetime.fromtimestamp(
acces_token.get("exp", 0)
),
"access_token": response_json.get("access_token", ""),
"refresh_token": response_json.get("refresh_token", ""),
}
Expand Down
3 changes: 1 addition & 2 deletions l10n_ro_account_anaf_sync/data/neutralize.sql
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
UPDATE l10n_ro_account_anaf_sync
SET state = 'test', anaf_einvoice_sync_url = 'https://api.anaf.ro/test/FCTEL/rest';
UPDATE l10n_ro_account_anaf_sync_scope SET state = 'test';
1 change: 1 addition & 0 deletions l10n_ro_account_anaf_sync/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from . import l10n_ro_account_anaf_sync
from . import l10n_ro_account_anaf_sync_scope
from . import res_company
126 changes: 53 additions & 73 deletions l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

import logging
from datetime import timedelta
from datetime import datetime

import jwt
import requests

from odoo import _, api, fields, models
from odoo import _, fields, models
from odoo.exceptions import UserError

_logger = logging.getLogger(__name__)
Expand All @@ -17,14 +18,6 @@ class AccountANAFSync(models.Model):
_inherit = ["mail.thread", "l10n.ro.mixin"]
_description = "Account ANAF Sync"

_sql_constraints = [
(
"company_id_uniq",
"unique(company_id)",
"Another ANAF sync for this company already exists!",
),
]

def name_get(self):
result = []
for anaf_sync in self:
Expand Down Expand Up @@ -66,11 +59,13 @@ def name_get(self):
help="Time when was last time pressed the Get Token From Anaf Website."
" It waits for ANAF request for maximum 1 minute",
)
anaf_einvoice_sync_url = fields.Char(default="https://api.anaf.ro/test/FCTEL/rest")
state = fields.Selection(
[("test", "Test"), ("automatic", "Automatic")],
default="test",
)
anaf_scope_ids = fields.One2many(
comodel_name="l10n.ro.account.anaf.sync.scope", inverse_name="anaf_sync_id"
)

def write(self, values):
if values.get("company_id"):
Expand Down Expand Up @@ -105,6 +100,28 @@ def get_token_from_anaf_website(self):
"target": "new",
}

def _anaf_call_update_token(self, response):
# Extrage token-ul de acces din răspunsul JSON
token_data = response.json()
acces_token = {}

Check warning on line 106 in l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py

View check run for this annotation

Codecov / codecov/patch

l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py#L105-L106

Added lines #L105 - L106 were not covered by tests
if token_data.get("access_token", None):
acces_token = jwt.decode(

Check warning on line 108 in l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py

View check run for this annotation

Codecov / codecov/patch

l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py#L108

Added line #L108 was not covered by tests
token_data.get("access_token"),
algorithms=["RS512"],
options={"verify_signature": False},
)
vals = {}

Check warning on line 113 in l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py

View check run for this annotation

Codecov / codecov/patch

l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py#L113

Added line #L113 was not covered by tests
if token_data.get("access_token"):
vals["access_token"] = token_data.get("access_token")

Check warning on line 115 in l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py

View check run for this annotation

Codecov / codecov/patch

l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py#L115

Added line #L115 was not covered by tests
if token_data.get("refresh_token"):
vals["refresh_token"] = token_data.get("refresh_token")

Check warning on line 117 in l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py

View check run for this annotation

Codecov / codecov/patch

l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py#L117

Added line #L117 was not covered by tests
if acces_token.get("exp"):
vals["client_token_valability"] = datetime.fromtimestamp(

Check warning on line 119 in l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py

View check run for this annotation

Codecov / codecov/patch

l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py#L119

Added line #L119 was not covered by tests
acces_token.get("exp", 0)
)
vals["last_request_datetime"] = fields.Datetime.now()
self.write(vals)

Check warning on line 123 in l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py

View check run for this annotation

Codecov / codecov/patch

l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py#L122-L123

Added lines #L122 - L123 were not covered by tests

def handle_anaf_callback(self, authorization_code):
# Folosește codul de autorizare pentru a obține token-ul de acces
token_url = f"{self.anaf_oauth_url}/token"
Expand All @@ -114,6 +131,7 @@ def handle_anaf_callback(self, authorization_code):
"client_id": self.client_id,
"client_secret": self.client_secret,
"redirect_uri": self.anaf_callback_url,
"token_content_type": "jwt",
}

response = requests.post(
Expand All @@ -124,17 +142,31 @@ def handle_anaf_callback(self, authorization_code):
)

if response.status_code == 200:
# Extrage token-ul de acces din răspunsul JSON
token_data = response.json()
self.write(
{
"access_token": token_data.get("access_token"),
"refresh_token": token_data.get("refresh_token"),
"client_token_valability": fields.Date.today()
+ timedelta(days=90), # Valabilitate de 90 de zile
"last_request_datetime": fields.Datetime.now(),
}
self._anaf_call_update_token(response)

Check warning on line 145 in l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py

View check run for this annotation

Codecov / codecov/patch

l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py#L145

Added line #L145 was not covered by tests

def refresh_access_token(self):
self.ensure_one()

Check warning on line 148 in l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py

View check run for this annotation

Codecov / codecov/patch

l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py#L148

Added line #L148 was not covered by tests
if not self.refresh_token:
raise UserError(

Check warning on line 150 in l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py

View check run for this annotation

Codecov / codecov/patch

l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py#L150

Added line #L150 was not covered by tests
_("You don't have ANAF refresh token. Please get it first.")
)
token_url = f"{self.anaf_oauth_url}/token"
data = {

Check warning on line 154 in l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py

View check run for this annotation

Codecov / codecov/patch

l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py#L153-L154

Added lines #L153 - L154 were not covered by tests
"grant_type": "refresh_token",
"refresh_token": self.refresh_token,
"client_id": self.client_id,
"client_secret": self.client_secret,
}

response = requests.post(

Check warning on line 161 in l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py

View check run for this annotation

Codecov / codecov/patch

l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py#L161

Added line #L161 was not covered by tests
token_url,
data=data,
timeout=80,
headers={"Content-Type": "application/x-www-form-urlencoded"},
)

if response.status_code == 200:
self._anaf_call_update_token(response)

Check warning on line 169 in l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py

View check run for this annotation

Codecov / codecov/patch

l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync.py#L169

Added line #L169 was not covered by tests

def revoke_access_token(self):
self.ensure_one()
Expand All @@ -144,7 +176,6 @@ def revoke_access_token(self):
"client_id": self.client_id,
"client_secret": self.client_secret,
"access_token": self.access_token,
# "refresh_token": should function for refresh function
"token_type_hint": "access_token", # refresh_token (should work without)
}
url = self.anaf_oauth_url + "/revoke"
Expand Down Expand Up @@ -190,54 +221,3 @@ def test_anaf_api(self):
else:
message = _("Test token response: %s") % response.reason
self.message_post(body=message)

@api.onchange("state")
def _onchange_state(self):
if self.state:
if self.state == "test":
new_url = "https://api.anaf.ro/test/FCTEL/rest"
else:
new_url = "https://api.anaf.ro/prod/FCTEL/rest"
self.anaf_einvoice_sync_url = new_url

def _l10n_ro_einvoice_call(self, func, params, data=None, method="POST"):
self.ensure_one()
_logger.info("ANAF API call: %s %s" % (func, params))
url = self.anaf_einvoice_sync_url + func
access_token = self.access_token
headers = {
"Content-Type": "application/xml",
"Authorization": f"Bearer {access_token}",
}
test_data = self.env.context.get("test_data", False)
if test_data:
content = test_data
status_code = 200
else:
if method == "GET":
response = requests.get(
url, params=params, data=data, headers=headers, timeout=80
)
else:
response = requests.post(
url, params=params, data=data, headers=headers, timeout=80
)

content = response.content
status_code = response.status_code
if response.status_code == 400:
content = response.json()
content_type = ""
if response.headers:
content_type = response.headers.get("Content-Type", "")
if content_type == "application/xml":
_logger.info("ANAF API response: %s" % response.text)
if "text/plain" in content_type:
try:
content = response.json()
if content.get("eroare"):
status_code = 400
except Exception:
_logger.info("ANAF API response: %s" % response.text)

return content, status_code
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright (C) 2024 Dakai Soft
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo import api, fields, models


class AccountANAFSyncScope(models.Model):
_name = "l10n.ro.account.anaf.sync.scope"
_inherit = ["mail.thread", "l10n.ro.mixin"]
_description = "Account ANAF Sync Scope"

anaf_sync_id = fields.Many2one("l10n.ro.account.anaf.sync")
company_id = fields.Many2one(related="anaf_sync_id.company_id", store=True)
scope = fields.Selection([])
anaf_sync_production_url = fields.Char(string="API production URL")
anaf_sync_test_url = fields.Char(string="API test URL")
state = fields.Selection(
[("test", "Test"), ("production", "Production")],
default="test",
)
anaf_sync_url = fields.Char(compute="_compute_anaf_sync_url")

@api.depends("state")
def _compute_anaf_sync_url(self):
for entry in self:
entry.anaf_sync_url = getattr(
entry, f"anaf_sync_{entry.state}_url", "anaf_sync_test_url"
)

@api.onchange("scope")
def _onchange_scope(self):
self._compute_anaf_sync_url()

Check warning on line 32 in l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync_scope.py

View check run for this annotation

Codecov / codecov/patch

l10n_ro_account_anaf_sync/models/l10n_ro_account_anaf_sync_scope.py#L32

Added line #L32 was not covered by tests
28 changes: 16 additions & 12 deletions l10n_ro_account_anaf_sync/models/res_company.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,19 @@
class ResCompany(models.Model):
_inherit = "res.company"

l10n_ro_account_anaf_sync_id = fields.Many2one(
"l10n.ro.account.anaf.sync",
string="Romania - Account ANAF Sync",
compute="_compute_l10n_ro_account_anaf_sync_id",
)

def _compute_l10n_ro_account_anaf_sync_id(self):
for company in self:
domain = [("company_id", "=", company.id)]
company.l10n_ro_account_anaf_sync_id = self.env[
"l10n.ro.account.anaf.sync"
].search(domain, limit=1)
def _l10n_ro_get_anaf_sync(self, scope=None):
anaf_sync_scope = self.env["l10n.ro.account.anaf.sync.scope"]
if scope:
anaf_sync_scope |= anaf_sync_scope.search(
[
("anaf_sync_id.company_id", "=", self.id),
("scope", "=", scope),
(
"anaf_sync_id.client_token_valability",
">",
fields.Date().today(),
),
],
limit=1,
)
return anaf_sync_scope
1 change: 1 addition & 0 deletions l10n_ro_account_anaf_sync/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PyJWT
2 changes: 2 additions & 0 deletions l10n_ro_account_anaf_sync/security/ir.model.access.csv
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_l10n_ro_account_anaf_sync_user,l10n.ro.account.anaf.sync.user,model_l10n_ro_account_anaf_sync,account.group_account_user,1,0,0,0
access_l10n_ro_account_anaf_sync_invoice,l10n.ro.account.anaf.sync.invoice,model_l10n_ro_account_anaf_sync,account.group_account_invoice,1,0,0,0
access_l10n_ro_account_anaf_sync_manager,l10n.ro.account.anaf.sync.manager,model_l10n_ro_account_anaf_sync,account.group_account_manager,1,1,1,1
access_l10n_ro_account_anaf_sync_scope_user,l10n.ro.account.anaf.sync.scope.invoice,model_l10n_ro_account_anaf_sync_scope,account.group_account_invoice,1,0,0,0
access_l10n_ro_account_anaf_sync_scope_manager,l10n.ro.account.anaf.sync.scope.manager,model_l10n_ro_account_anaf_sync_scope,account.group_account_manager,1,1,1,1
3 changes: 2 additions & 1 deletion l10n_ro_account_anaf_sync/static/description/index.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<?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>
Expand Down Expand Up @@ -366,7 +367,7 @@ <h1 class="title">Romania - Account ANAF Sync</h1>
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:fd3e4bff53bd0db2cd49ee906caa77ecc3b2f6aae1c62fac37ae91e3193e870e
!! source digest: sha256:5101958b635714560aaac5b75a11aa44a5acaf7311a31f5dff0c5472d4a4b3e6
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Mature" src="https://img.shields.io/badge/maturity-Mature-brightgreen.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/l10n-romania/tree/16.0/l10n_ro_account_anaf_sync"><img alt="OCA/l10n-romania" src="https://img.shields.io/badge/github-OCA%2Fl10n--romania-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/l10n-romania-16-0/l10n-romania-16-0-l10n_ro_account_anaf_sync"><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/l10n-romania&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 will make posible to send e-invoice / e-transport to Romanian goverment anaf.ro.</p>
Expand Down
Loading

0 comments on commit 0df3866

Please sign in to comment.