Skip to content

Commit

Permalink
Merge 21427b7 into bd10020
Browse files Browse the repository at this point in the history
  • Loading branch information
javikalsan committed May 22, 2024
2 parents bd10020 + 21427b7 commit b907d50
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 48 deletions.
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.md
*.yaml
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Change log

## Unreleased

- Invoices
- Custom timeout for some ERP queries
- Report invoice zip timeout
- Single zip for many invoices

## 0.7.0 (2024-05-15)

- Responsive invoice page
Expand Down
11 changes: 10 additions & 1 deletion backend/api_business.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
invoices_zip,
production_data,
)
from .erp import ErpConnectionError
from .erp import ErpConnectionError, ErpTimeoutError
from .auth import validated_user
from .utils.responses import PdfStreamingResponse, ZipStreamingResponse
from consolemsg import error
Expand All @@ -38,6 +38,15 @@ async def erp_connection_error_handler(request: Request, exc: ErpConnectionError
content={"details": "Unable to reach ERP"},
)

@app.exception_handler(ErpTimeoutError)
async def erp_timeout_error_handler(request: Request, exc: ErpTimeoutError):
# TODO: Log exception
error("ERP timeout")
return JSONResponse(
status_code=status.HTTP_504_GATEWAY_TIMEOUT,
content={"details": "ERP timeout"},
)

@app.get('/api/me')
def api_profile_information(user: dict = Depends(validated_user)) -> UserProfile:
return profile_info(user)
Expand Down
19 changes: 14 additions & 5 deletions backend/erp.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# TODO: This is a quick proof of concept
# TODO; Use erppeek, connection pool management, transactions...

ERP_DEFAULT_TIMEOUT_SECONDS = 5
ERP_INVOICE_ZIP_TIMEOUT_SECONDS = 30

@decorator
def requires_token(f, self, *args, **kwds):
Expand All @@ -21,6 +23,10 @@ class ErpConnectionError(Exception):
pass


class ErpTimeoutError(Exception):
pass


class ErpUnexpectedError(Exception):
def __init__(self, remote_error, remote_traceback):
self.remote_error = remote_error
Expand All @@ -41,10 +47,12 @@ def __init__(self):
self.debug = os.environ.get("ERP_DEBUG", 'False').lower() in ('true', '1', 't')


def _post(self, endpoint, *args):
def _post(self, endpoint, *args, timeout=ERP_DEFAULT_TIMEOUT_SECONDS):
if self.debug: print(">>", endpoint, args)
try:
r = httpx.post(self.baseurl + endpoint, json=list(args), timeout=10)
r = httpx.post(self.baseurl + endpoint, json=list(args), timeout=timeout)
except httpx.ReadTimeout as e:
raise ErpTimeoutError(str(e))
except httpx.ConnectError as e:
raise ErpConnectionError(str(e))
r.raise_for_status()
Expand All @@ -63,8 +71,8 @@ def token(self):
self._token = self._post("/common", "token", self.db, self.user, self.password)

@requires_token
def object_execute(self, *args):
return self._post("/object", "execute", self.db, "token", self._token, *args)
def object_execute(self, *args, timeout=10):
return self._post("/object", "execute", self.db, "token", self._token, *args, timeout=timeout)

def customer_list(self):
ids = self.object_execute("res.partner", "search", [["vat", "<>", False]])
Expand Down Expand Up @@ -138,7 +146,8 @@ def invoice_pdf(self, username: str, invoice_number: str) -> dict:

def invoices_zip(self, username: str, invoice_numbers: list[str]) -> dict:
return self.object_execute(
"som.ov.invoices", "download_invoices_zip", username, invoice_numbers
"som.ov.invoices", "download_invoices_zip", username, invoice_numbers,
timeout=ERP_INVOICE_ZIP_TIMEOUT_SECONDS,
)

def production_data(
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/data/dummyinstallationdetail.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ installation_details:
province: 'Girona'
rated_power: 1000
technology: false
type: 'IT-00666'
type: 'IT-00666'
2 changes: 1 addition & 1 deletion frontend/src/data/dummyinstallations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@
- contract_number: '190001_57'
installation_name: Torrefarrera
- contract_number: '190001_37'
installation_name: Torrefarrera
installation_name: Torrefarrera
32 changes: 16 additions & 16 deletions frontend/src/data/dummyinvoices.yaml
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
- invoice_number: F000001
contract_number: "19000_1"
emission_date: "2022-03-01"
first_period_date: "2022-01-01"
last_period_date: "2022-02-01"
contract_number: '19000_1'
emission_date: '2022-03-01'
first_period_date: '2022-01-01'
last_period_date: '2022-02-01'
amount: 10.50
concept: market
liquidation: null
payment_status: paid
- invoice_number: F000013
contract_number: "19000_1"
emission_date: "2022-04-01"
first_period_date: "2022-02-01"
last_period_date: "2022-03-01"
contract_number: '19000_1'
emission_date: '2022-04-01'
first_period_date: '2022-02-01'
last_period_date: '2022-03-01'
amount: 320.53
concept: specific_retribution
liquidation: null
payment_status: open
- invoice_number: F000003
contract_number: "19000_2"
emission_date: "2022-03-01"
first_period_date: "2022-01-01"
last_period_date: "2022-02-01"
contract_number: '19000_2'
emission_date: '2022-03-01'
first_period_date: '2022-01-01'
last_period_date: '2022-02-01'
amount: 1300.12
concept: services
liquidation: null
payment_status: unpaid
- invoice_number: F000017
contract_number: "19000_2"
emission_date: "2022-04-01"
first_period_date: "2022-02-01"
last_period_date: "2022-03-01"
contract_number: '19000_2'
emission_date: '2022-04-01'
first_period_date: '2022-02-01'
last_period_date: '2022-03-01'
amount: 523.00
concept: null
liquidation: null
Expand Down
1 change: 1 addition & 0 deletions frontend/src/i18n/locale-es.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ OVAPI:
ERR_NETWORK: No se encuentra el servidor. ¿Tienes conexión a la red?
ERR_GATEWAY: El sistema no está disponible por mantenimiento. Prueba más tarde.
ERR_INTERNAL: 'Se ha producido un error inesperado en el servidor. Referencia {{reference}}.'
ERR_TOO_MANY_INVOICES: Petición demasiado grande, prueba con menos facturas.
INSTALLATION_DETAIL:
DETAILS_TITLE: Detalles
INSTALLATION_DETAILS_TITLE: Detalles de la instalación
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/InvoicesPage/DownloadButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default function DownloadButton({
} catch (error) {
setState(done)
setError(error)
messages.error(error, { context: 'Downloading' })
messages.error(error.error, { context: 'Downloading' })
}
}
async function handleClick(ev) {
Expand Down
61 changes: 38 additions & 23 deletions frontend/src/services/ovapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ function handleCommonErrors(context) {
const t = i18n.t

console.log(`Error ${error.code} ${context}\n${error.message}`)
// UI -> API network problem
if (error.code === 'ERR_NETWORK') {
messages.error(t('OVAPI.ERR_NETWORK'), { context })
return {
Expand Down Expand Up @@ -289,33 +290,47 @@ function invoicePdf(invoiceNumber) {
})
}

function handleInvoiceZipDownloadTimeout(context) {
const t = i18n.t

return (error) => {
if (error.response) {
// Gateway timeout (too many invoices)
if (error.response.status === 504) {
messages.error(t('OVAPI.ERR_TOO_MANY_INVOICES'), { context })
return {
error: t('OVAPI.ERR_TOO_MANY_INVOICES'),
context,
}
}
}
throw error
}
}
function invoicesZip(invoiceNumbers) {
const chunkSize = 12 //creates zip of 12 invoices (1 year)
for (let i = 0; i < invoiceNumbers.length; i += chunkSize) {
const chunk = invoiceNumbers.slice(i, i + chunkSize)
const context = i18n.t('OVAPI.CONTEXT_INVOICES_ZIP_DOWNLOAD', {
invoice_numbers: chunk,
})
const context = i18n.t('OVAPI.CONTEXT_INVOICES_ZIP_DOWNLOAD', {
invoice_numbers: invoiceNumbers,
})

const queryParams = '?invoice_numbers=' + chunk
const queryParams = '?invoice_numbers=' + invoiceNumbers

axios
.get(`/api/invoices/zip${queryParams}`, {
responseType: 'arraybuffer',
})
.catch(handleCommonErrors(context))
.catch(handleRemainingErrors(context))
.then((result) => {
if (result.error !== undefined) {
throw result
}
return axios
.get(`/api/invoices/zip${queryParams}`, {
responseType: 'arraybuffer',
})
.catch(handleCommonErrors(context))
.catch(handleInvoiceZipDownloadTimeout())
.catch(handleRemainingErrors(context))
.then((result) => {
if (result.error !== undefined) {
throw result
}

const filename =
result.headers['content-disposition']?.match(/filename="([^"]+)"/)[1] ??
`facturas-from${chunk[0]}.zip`
downloadBlob(filename, result.data, 'application/zip')
})
}
const filename =
result.headers['content-disposition']?.match(/filename="([^"]+)"/)[1] ??
`facturas_from_${invoiceNumbers[0]}.zip`
downloadBlob(filename, result.data, 'application/zip')
})
}

async function productionData(first_timestamp_utc, last_timestamp_utc, contract_number) {
Expand Down

0 comments on commit b907d50

Please sign in to comment.