Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reimbursement portal added #269

Open
wants to merge 1 commit into
base: 17.0-1.2
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ addon | version | maintainers | summary
[g2p_program_reimbursement](g2p_program_reimbursement/) | 17.0.0.0.0 | | OpenG2P Programs: Reimbursement
[g2p_programs](g2p_programs/) | 17.0.0.0.0 | | OpenG2P Programs
[g2p_proxy_means_test](g2p_proxy_means_test/) | 17.0.0.0.0 | | G2P: Proxy Means Test
[g2p_reimbursement_portal](g2p_reimbursement_portal/) | 17.0.0.0.0 | | G2P Reimbursement Portal
[g2p_social_registry_importer](g2p_social_registry_importer/) | 17.0.0.0.0 | | Import records from Social Registry
[g2p_theme](g2p_theme/) | 17.0.0.0.0 | | OpenG2P Theme

Expand Down
57 changes: 57 additions & 0 deletions g2p_reimbursement_portal/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
========================
G2P Reimbursement Portal
========================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:4bbe1a58e66a83399a043122ac92dd023b8d6b6737dfb3c48bbd318fa0ccb5a0
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
:target: https://odoo-community.org/page/development-status
:alt: Alpha
.. |badge2| image:: https://img.shields.io/badge/github-OpenG2P%2Fopeng2p--program-lightgray.png?logo=github
:target: https://github.com/OpenG2P/openg2p-program/tree/17.0-develop/g2p_reimbursement_portal
:alt: OpenG2P/openg2p-program

|badge1| |badge2|

OpenG2P Reimbursement Portal

.. IMPORTANT::
This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
`More details on development status <https://odoo-community.org/page/development-status>`_

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OpenG2P/openg2p-program/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/OpenG2P/openg2p-program/issues/new?body=module:%20g2p_reimbursement_portal%0Aversion:%2017.0-develop%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
~~~~~~~

* OpenG2P

Maintainers
~~~~~~~~~~~

This module is part of the `OpenG2P/openg2p-program <https://github.com/OpenG2P/openg2p-program/tree/17.0-develop/g2p_reimbursement_portal>`_ project on GitHub.

You are welcome to contribute.
3 changes: 3 additions & 0 deletions g2p_reimbursement_portal/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Part of OpenG2P. See LICENSE file for full copyright and licensing details.
from . import models
from . import controllers
34 changes: 34 additions & 0 deletions g2p_reimbursement_portal/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "G2P Reimbursement Portal",
"category": "G2P",
"version": "17.0.0.0.0",
"sequence": 1,
"author": "OpenG2P",
"website": "https://openg2p.org",
"license": "LGPL-3",
"development_status": "Alpha",
"depends": ["g2p_program_reimbursement", "g2p_agent_portal_base", "g2p_program_cycleless"],
"data": [
"data/g2p_reimbursement_portal_form_data.xml",
"views/g2p_portal_reimbursement.xml",
"views/g2p_portal_form_template.xml",
"views/g2p_portal_form_submitted.xml",
"views/g2p_portal_dashboard.xml",
"views/base.xml",
"views/home.xml",
"views/login.xml",
"views/profile.xml",
"views/website_page.xml",
"views/menu_view.xml",
],
"assets": {
"website.assets_wysiwyg": [
"g2p_reimbursement_portal/static/src/js/reim_form_editor.js",
],
},
"demo": [],
"images": [],
"application": True,
"installable": True,
"auto_install": False,
}
1 change: 1 addition & 0 deletions g2p_reimbursement_portal/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import main
285 changes: 285 additions & 0 deletions g2p_reimbursement_portal/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
import json
import logging

from werkzeug.datastructures import FileStorage
from werkzeug.exceptions import Forbidden

from odoo import http
from odoo.http import request

from odoo.addons.g2p_agent_portal_base.controllers.main import AgentPortalBase

_logger = logging.getLogger(__name__)


class ReimbursementPortal(AgentPortalBase):
@http.route(["/portal/reimbursement/voucher"], type="http", auth="user", website=True)
def portal_new_entitlements(self, **kwargs):
self.check_roles("Portal")
partner_id = request.env.user.partner_id
entitlements = (
request.env["g2p.entitlement"]
.sudo()
.search(
[
("service_provider_id", "=", partner_id.id),
("state", "=", "approved"),
]
)
)

values = []
for entitlement in entitlements:
# to check no reimbursement claims are already made against this entitlement
is_submitted = len(entitlement.reimbursement_entitlement_ids) > 0
reimbursement_program = entitlement.program_id.reimbursement_program_id
values.append(
{
"entitlement_id": entitlement.id,
"program_name": entitlement.program_id.name,
"beneficiary_name": entitlement.partner_id.name,
"initial_amount": entitlement.initial_amount,
"is_submitted": is_submitted,
"status": "New" if not is_submitted else entitlement.reimbursement_entitlement_ids.state,
"is_form_mapped": getattr(reimbursement_program, "self_service_portal_form", False)
is not None,
}
)

return request.render(
"g2p_reimbursement_portal.reimbursements_entitlement_view",
{
"entitlements": values,
},
)

@http.route(
["/portal/reimbursement/voucher/<int:_id>"],
type="http",
auth="user",
website=True,
)
def portal_new_submission(self, _id, **kwargs):
self.check_roles("Portal")

current_partner = request.env.user.partner_id

entitlement = request.env["g2p.entitlement"].sudo().browse(_id)
beneficiary = entitlement.partner_id

if entitlement.service_provider_id.id != current_partner.id or entitlement.state != "approved":
raise Forbidden()

# file_size = entitlement.program_id.reimbursement_program_id.file_size_spp

# check if already claimed
if len(entitlement.reimbursement_entitlement_ids) > 0:
return request.redirect(f"/portal/reimbursement/claim/{_id}")

# view = entitlement.program_id.reimbursement_program_id.self_service_portal_form.view_id

return request.render(
"g2p_reimbursement_portal.reimbursement_form_template_view",
{
"entitlement_id": _id,
"current_partner_name": current_partner.given_name.capitalize()
+ ", "
+ current_partner.family_name.capitalize()
if current_partner.given_name and current_partner.family_name
else current_partner.name.capitalize(),
"beneficiary": beneficiary,
# "file_size": file_size,
},
)

@http.route(
["/portal/reimbursement/submit/<int:_id>"],
type="http",
auth="user",
website=True,
csrf=False,
)
def portal_claim_submission(self, _id, **kwargs):
self.check_roles("Portal")

current_partner = request.env.user.partner_id

# TODO: get only issued entitlements

entitlement = request.env["g2p.entitlement"].sudo().browse(_id)
if entitlement.service_provider_id.id != current_partner.id or entitlement.state != "approved":
raise Forbidden()

if request.httprequest.method == "POST":
form_data = kwargs
# check if already claimed
if len(entitlement.reimbursement_entitlement_ids) > 0:
return request.redirect(f"/portal/reimbursement/claim/{_id}")

# TODO: allow resubmission

# TODO: Check if reimbursement program mapped to original program

current_partner_membership = current_partner.program_membership_ids.filtered(
lambda x: x.program_id.id == entitlement.program_id.reimbursement_program_id.id
)
# TODO: Check current partner not part of prog memberships of
# reimbursement program.

supporting_documents_store = (
entitlement.program_id.reimbursement_program_id.supporting_documents_store
)

# TODO: remove all hardcoding in the next lines
received_code = form_data.get("voucher_code", None)
actual_amount = form_data.get("initial_amount", None)

document_details = {}
for key in kwargs:
if isinstance(kwargs[key], FileStorage):
document_details[key] = request.httprequest.files.getlist(key)

supporting_document_files = self.process_documents(
document_details,
supporting_documents_store,
membership=current_partner_membership,
)
if not supporting_document_files:
_logger.warning("Empty/No File received for field %s", "Statement of Account")
supporting_document_file_ids = None
else:
supporting_document_file_ids = []

# saving the multiple document id
for document_id in supporting_document_files:
supporting_document_file_ids.append(document_id.get("document_id", None))

reimbursement_claim = entitlement.submit_reimbursement_claim(
current_partner,
received_code,
supporting_document_file_ids=supporting_document_file_ids
if supporting_document_file_ids
else None,
amount=actual_amount,
)
if reimbursement_claim == (2, None):
_logger.error("Not a valid Voucher Code")
return request.redirect(f"/portal/reimbursement/voucher/{_id}")

else:
# TODO: search and return currently active claim
# TODO: Check whether entitlement.reimbursement_entitlement_ids[0].partner_id is same as current
if len(entitlement.reimbursement_entitlement_ids) == 0:
return request.redirect(f"/portal/reimbursement/entitlement/{_id}")

return request.redirect(f"/portal/reimbursement/claim/{_id}")

@http.route(
["/portal/reimbursement/claim/<int:_id>"],
type="http",
auth="user",
website=True,
)
def portal_post_submission(self, _id, **kwargs):
self.check_roles("Portal")

entitlement = request.env["g2p.entitlement"].sudo().browse(_id)
current_partner = request.env.user.partner_id

reimbursement_claim = (
request.env["g2p.entitlement"]
.sudo()
.search(
[
("partner_id", "=", current_partner.id),
("reimbursement_original_entitlement_id", "=", entitlement.id),
]
)
)

if len(reimbursement_claim) < 1:
return request.redirect(f"/portal/reimbursement/entitlement/{_id}")

return request.render(
"g2p_reimbursement_portal.reimbursement_form_submitted",
{
"entitlement": entitlement.id,
"submission_date": reimbursement_claim.create_date.strftime("%d-%b-%Y"),
"approved_date": reimbursement_claim.date_approved,
"application_id": reimbursement_claim.id,
"application_status": reimbursement_claim.state,
"user": current_partner.given_name.capitalize()
+ " "
+ current_partner.family_name.capitalize()
if current_partner.given_name and current_partner.family_name
else current_partner.name.capitalize(),
},
)

@http.route("/portal/reimbursement/get_voucher_codes", type="http", auth="user", website=True)
def get_voucher_codes(self):
entitlements = (
request.env["g2p.entitlement"]
.sudo()
.search([("service_provider_id", "=", request.env.user.partner_id.id)])
)

voucher_details = []
for entilement in entitlements:
voucher_details.append(
{
"beneficiary_name": entilement.partner_id.name,
"code": entilement.code,
}
)

return json.dumps(voucher_details)

@classmethod
def add_file_to_store(cls, files, store, program_membership=None, tags=None):
if isinstance(files, FileStorage):
files = [
files,
]
file_details = []
for file in files:
if store and file.filename:
if len(file.filename.split(".")) > 1:
supporting_document_ext = "." + file.filename.split(".")[-1]
else:
supporting_document_ext = None
document_file = store.add_file(
file.stream.read(),
extension=supporting_document_ext,
program_membership=program_membership,
tags=tags,
)
document_uuid = document_file.name.split(".")[0]
file_details.append(
{
"document_id": document_file.id,
"document_uuid": document_uuid,
"document_name": document_file.name,
"document_slug": document_file.slug,
"document_url": document_file.url,
}
)
return file_details

def get_field_to_exclude(self, data):
current_partner = request.env.user.partner_id
keys = []
for key in data:
if key in current_partner:
current_partner[key] = data[key]
keys.append(key)

return keys

def process_documents(self, documents, store, membership):
all_file_details = []
for tag, document in documents.items():
all_file_details += self.add_file_to_store(
document, store, program_membership=membership, tags=tag
)
return all_file_details
Loading
Loading