Skip to content
Snippets Groups Projects
Commit 6c3fa408 authored by sks444's avatar sks444
Browse files

Add vendor detail page

parent 1d2d8166
No related branches found
No related tags found
No related merge requests found
# Generated by Django 2.2.24 on 2021-06-22 10:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('application_projects', '0038_rename_verbose_name_of_vendor_form_settings'),
]
operations = [
migrations.AddField(
model_name='projectsettings',
name='vendor_setup_required',
field=models.BooleanField(default=True),
),
]
# Generated by Django 2.2.24 on 2021-06-30 04:18
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('application_projects', '0039_add_required_vendor_setup_field_to_project_settings'),
]
operations = [
migrations.AddField(
model_name='vendor',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Creation time'),
preserve_default=False,
),
migrations.AddField(
model_name='vendor',
name='updated_at',
field=models.DateTimeField(auto_now=True, verbose_name='Update time'),
),
]
...@@ -372,6 +372,7 @@ class ProjectApprovalForm(BaseStreamForm, models.Model): ...@@ -372,6 +372,7 @@ class ProjectApprovalForm(BaseStreamForm, models.Model):
@register_setting @register_setting
class ProjectSettings(BaseSetting): class ProjectSettings(BaseSetting):
compliance_email = models.TextField("Compliance Email") compliance_email = models.TextField("Compliance Email")
vendor_setup_required = models.BooleanField(default=True)
class Approval(models.Model): class Approval(models.Model):
......
...@@ -45,7 +45,8 @@ class Vendor(models.Model): ...@@ -45,7 +45,8 @@ class Vendor(models.Model):
('organization', _('Yes, the account belongs to the organisation above')), ('organization', _('Yes, the account belongs to the organisation above')),
('personal', _('No, it is a personal bank account')), ('personal', _('No, it is a personal bank account')),
] ]
created_at = models.DateTimeField(verbose_name=_('Creation time'), auto_now_add=True)
updated_at = models.DateTimeField(verbose_name=_('Update time'), auto_now=True)
user = models.OneToOneField( user = models.OneToOneField(
settings.AUTH_USER_MODEL, on_delete=models.PROTECT, settings.AUTH_USER_MODEL, on_delete=models.PROTECT,
related_name='vendor' related_name='vendor'
......
...@@ -19,16 +19,18 @@ ...@@ -19,16 +19,18 @@
{% endif %} {% endif %}
</div> </div>
</li> </li>
{% if settings.application_projects.ProjectSettings.vendor_setup_required and project.vendor %}
<li class="docs-block__row"> <li class="docs-block__row">
<div class="docs-block__row-inner"> <div class="docs-block__row-inner">
<svg class="icon docs-block__icon"><use xlink:href="#tick"></use></svg> <svg class="icon docs-block__icon"><use xlink:href="#tick"></use></svg>
<p class="docs-block__title">Contractor Setup Form</p> <p class="docs-block__title">Contractor Setup Form</p>
</div> </div>
<div class="docs-block__row-inner"> <div class="docs-block__row-inner">
<a class="docs-block__link" href="{% url 'apply:projects:vendor' pk=project.pk %}">Update info</a> <a class="docs-block__link" href="{% url 'apply:projects:vendor-detail' pk=project.pk vendor_pk=project.vendor.pk %}">View</a>
</div> <a class="docs-block__link" href="{% url 'apply:projects:vendor' pk=project.pk %}">Update info</a>
</li> </div>
</li>
{% endif %}
<li class="docs-block__row"> <li class="docs-block__row">
<div class="docs-block__row-inner"> <div class="docs-block__row-inner">
......
{% extends "base-apply.html" %}
{% load bleach_tags %}
{% block title %}Vendor Info for {{ project.title }} {% endblock %}
{% block content %}
<div class="admin-bar">
<div class="admin-bar__inner">
<h2 class="heading heading--no-margin">Vendor Information for {{ project.title }}</h2>
</div>
</div>
<div class="grid">
<div>
<h5 class="vendor-info">Last Updated: {{ vendor.updated_at|date:'F d, Y' }}</h5>
</div>
<div>
<a class="link link--edit-vendor is-active" href="{% url 'apply:projects:vendor' pk=project.pk %}">
Edit
<svg class="icon icon--pen"><use xlink:href="#pen"></use></svg>
</a>
</div>
</div>
<div class="rich-text rich-text--answers">
{% for group in vendor_detailed_response.values %}
{% if group.title %}
<h1>{{ group.title|bleach }}</h4>
{% endif %}
{% for question, answer in group.questions %}
<h5>{{ question }}</h5>
{% if question == 'Due Diligence Documents' %}
<div class="card card--solid">
<div class="card__inner">
<!-- <h5 class="card__heading">Reciepts</h5> -->
{% for document in due_diligence_documents %}
<p class="card__text"><a href="{% url "apply:projects:vendor-documents" pk=project.pk vendor_pk=project.vendor.pk file_pk=document.pk %}">{{ document.document.name }}</a></p>
{% endfor %}
</div>
</div>
{% else %}
<p>{% if answer %}{% if answer == True %}{{ answer|yesno:"Yes,No" }}{% else %}{{ answer|bleach }}{% endif %}{% else %}-{% endif %}</p>
{% endif %}
{% endfor %}
{% endfor %}
</div>
{% endblock %}
...@@ -21,6 +21,8 @@ from .views import ( ...@@ -21,6 +21,8 @@ from .views import (
ReportPrivateMedia, ReportPrivateMedia,
ReportSkipView, ReportSkipView,
ReportUpdateView, ReportUpdateView,
VendorDetailView,
VendorPrivateMediaView,
) )
app_name = 'projects' app_name = 'projects'
...@@ -37,6 +39,8 @@ urlpatterns = [ ...@@ -37,6 +39,8 @@ urlpatterns = [
path('simplified/', ProjectDetailSimplifiedView.as_view(), name='simplified'), path('simplified/', ProjectDetailSimplifiedView.as_view(), name='simplified'),
path('request/', CreatePaymentRequestView.as_view(), name='request'), path('request/', CreatePaymentRequestView.as_view(), name='request'),
path('vendor/', CreateVendorView.as_view(), name='vendor'), path('vendor/', CreateVendorView.as_view(), name='vendor'),
path('vendor/<int:vendor_pk>/', VendorDetailView.as_view(), name='vendor-detail'),
path('vendor/<int:vendor_pk>/documents/<int:file_pk>/', VendorPrivateMediaView.as_view(), name='vendor-documents'),
])), ])),
path('payment-requests/', include(([ path('payment-requests/', include(([
path('', PaymentRequestListView.as_view(), name='all'), path('', PaymentRequestListView.as_view(), name='all'),
......
...@@ -41,7 +41,7 @@ from .report import ( ...@@ -41,7 +41,7 @@ from .report import (
ReportSkipView, ReportSkipView,
ReportUpdateView, ReportUpdateView,
) )
from .vendor import CreateVendorView from .vendor import CreateVendorView, VendorDetailView, VendorPrivateMediaView
__all__ = [ __all__ = [
'ChangePaymentRequestStatusView', 'ChangePaymentRequestStatusView',
...@@ -82,4 +82,6 @@ __all__ = [ ...@@ -82,4 +82,6 @@ __all__ = [
'ReportFrequencyUpdate', 'ReportFrequencyUpdate',
'ReportListView', 'ReportListView',
'CreateVendorView', 'CreateVendorView',
'VendorDetailView',
'VendorPrivateMediaView',
] ]
from django.contrib.auth.models import User
from hypha.apply.projects.models.vendor import VendorFormSettings
import json import json
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import UserPassesTestMixin
from django.views.generic.detail import DetailView
from django.utils import timezone
from django.http import Http404
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.utils.decorators import method_decorator
from django.db.models.fields.files import FieldFile from django.db.models.fields.files import FieldFile
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from formtools.wizard.views import SessionWizardView from formtools.wizard.views import SessionWizardView
from wagtail.core.models import Site from wagtail.core.models import Site
from addressfield.fields import ADDRESS_FIELDS_ORDER
from hypha.apply.utils.storage import PrivateStorage from hypha.apply.utils.storage import PrivateStorage
from hypha.apply.utils.storage import PrivateMediaView
from ..forms import ( from ..forms import (
CreateVendorFormStep1, CreateVendorFormStep1,
...@@ -16,7 +24,7 @@ from ..forms import ( ...@@ -16,7 +24,7 @@ from ..forms import (
CreateVendorFormStep5, CreateVendorFormStep5,
CreateVendorFormStep6, CreateVendorFormStep6,
) )
from ..models import BankInformation, DueDiligenceDocument, Project from ..models import BankInformation, DueDiligenceDocument, Project, ProjectSettings, Vendor
def show_extra_info_form(wizard): def show_extra_info_form(wizard):
...@@ -28,13 +36,16 @@ def show_extra_info_form(wizard): ...@@ -28,13 +36,16 @@ def show_extra_info_form(wizard):
class VendorAccessMixin: class VendorAccessMixin:
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
project_settings = ProjectSettings.for_request(request)
if not project_settings.vendor_setup_required:
raise PermissionDenied
is_admin = request.user.is_apply_staff is_admin = request.user.is_apply_staff
project = self.get_project() project = self.get_project()
is_owner = request.user == project.user is_owner = request.user == project.user
if not (is_owner or is_admin): if not (is_owner or is_admin):
raise PermissionDenied raise PermissionDenied
if not project.vendor: if not project.vendor:
raise PermissionDenied raise Http404
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
...@@ -109,6 +120,7 @@ class CreateVendorView(VendorAccessMixin, SessionWizardView): ...@@ -109,6 +120,7 @@ class CreateVendorView(VendorAccessMixin, SessionWizardView):
vendor.contractor_name = cleaned_data['contractor_name'] vendor.contractor_name = cleaned_data['contractor_name']
vendor.type = cleaned_data['type'] vendor.type = cleaned_data['type']
vendor.required_to_pay_taxes = cleaned_data['required_to_pay_taxes'] vendor.required_to_pay_taxes = cleaned_data['required_to_pay_taxes']
vendor.updated_at = timezone.now()
vendor.save() vendor.save()
not_deleted_original_filenames = [ not_deleted_original_filenames = [
...@@ -176,3 +188,110 @@ class CreateVendorView(VendorAccessMixin, SessionWizardView): ...@@ -176,3 +188,110 @@ class CreateVendorView(VendorAccessMixin, SessionWizardView):
kwargs = super(CreateVendorView, self).get_form_kwargs(step) kwargs = super(CreateVendorView, self).get_form_kwargs(step)
kwargs['site'] = Site.find_for_request(self.request) kwargs['site'] = Site.find_for_request(self.request)
return kwargs return kwargs
class VendorDetailView(VendorAccessMixin, DetailView):
model = Vendor
template_name = 'application_projects/vendor_detail.html'
def get_object(self, queryset=None):
return get_object_or_404(self.model, id=self.kwargs['vendor_pk'])
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['vendor_detailed_response'] = self.get_detailed_response()
context['project'] = self.get_project()
vendor = self.get_object()
context['due_diligence_documents'] = vendor.due_diligence_documents.all()
return context
def get_project(self):
return get_object_or_404(Project, pk=self.kwargs['pk'])
def get_detailed_response(self):
vendor = self.get_object()
vendor_form_settings = VendorFormSettings.for_request(self.request)
data = {}
group = 0
data.setdefault(group, {'title': 'Vendor Information', 'questions': list()})
data[group]['questions'] = [
(getattr(vendor_form_settings, 'name_label'), vendor.name),
(getattr(vendor_form_settings, 'contractor_name_label'), vendor.contractor_name),
(getattr(vendor_form_settings, 'type_label'), vendor.type),
(getattr(vendor_form_settings, 'required_to_pay_taxes_label'), vendor.required_to_pay_taxes),
('Due Diligence Documents', ''),
]
group = group + 1
data.setdefault(group, {'title': 'Bank Account Information', 'questions': list()})
bank_info = vendor.bank_info
data[group]['questions'] = [
(getattr(vendor_form_settings, 'account_holder_name_label'), bank_info.account_holder_name if bank_info else ''),
(getattr(vendor_form_settings, 'account_routing_number_label'), bank_info.account_routing_number if bank_info else ''),
(getattr(vendor_form_settings, 'account_number_label'), bank_info.account_number if bank_info else ''),
(getattr(vendor_form_settings, 'account_currency_label'), bank_info.account_currency if bank_info else ''),
]
group = group + 1
data.setdefault(group, {'title': '(Optional) Extra Information for Accepting Payments', 'questions': list()})
data[group]['questions'] = [
(getattr(vendor_form_settings, 'branch_address_label'), self.get_address_display(bank_info.branch_address) if bank_info else ''),
]
group = group + 1
data.setdefault(group, {'title': 'Intermediary Bank Account Information', 'questions': list()})
iba_info = bank_info.iba_info if bank_info else None
data[group]['questions'] = [
(getattr(vendor_form_settings, 'ib_account_routing_number_label'), iba_info.account_routing_number if iba_info else ''),
(getattr(vendor_form_settings, 'ib_account_number_label'), iba_info.account_number if iba_info else ''),
(getattr(vendor_form_settings, 'ib_account_currency_label'), iba_info.account_currency if iba_info else ''),
(getattr(vendor_form_settings, 'ib_branch_address_label'), self.get_address_display(iba_info.branch_address) if iba_info else ''),
]
group = group + 1
data.setdefault(group, {'title': 'Account Holder National Identity Document Information', 'questions': list()})
data[group]['questions'] = [
(getattr(vendor_form_settings, 'nid_type_label'), bank_info.nid_type if bank_info else ''),
(getattr(vendor_form_settings, 'nid_number_label'), bank_info.nid_number if bank_info else ''),
]
group = group + 1
data.setdefault(group, {'title': None, 'questions': list()})
data[group]['questions'] = [
(getattr(vendor_form_settings, 'other_info_label'), vendor.other_info),
]
return data
def get_address_display(self, address):
try:
address = json.loads(address)
except (json.JSONDecodeError, AttributeError):
return ''
else:
return ', '.join(
address.get(field)
for field in ADDRESS_FIELDS_ORDER
if address.get(field)
)
@method_decorator(login_required, name='dispatch')
class VendorPrivateMediaView(UserPassesTestMixin, PrivateMediaView):
raise_exception = True
def dispatch(self, *args, **kwargs):
pk = self.kwargs['pk']
vendor_pk = self.kwargs['vendor_pk']
self.vendor = get_object_or_404(Vendor, pk=vendor_pk)
self.project = get_object_or_404(Project, pk=pk)
return super().dispatch(*args, **kwargs)
def get_media(self, *args, **kwargs):
file_pk = kwargs.get('file_pk')
document = get_object_or_404(self.vendor.due_diligence_documents, pk=file_pk)
return document.document
def test_func(self):
if self.request.user.is_apply_staff:
return True
if self.request.user == self.project.user:
return True
return False
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment