From 6c3fa4088f755920a553c6d0595900c6c9f2f18f Mon Sep 17 00:00:00 2001
From: sks444 <krishnasingh.ss30@gmail.com>
Date: Wed, 30 Jun 2021 17:08:03 +0530
Subject: [PATCH] Add vendor detail page

---
 ..._vendor_setup_field_to_project_settings.py |  18 +++
 .../0040_add_created_and_updated_at.py        |  25 ++++
 hypha/apply/projects/models/project.py        |   1 +
 hypha/apply/projects/models/vendor.py         |   3 +-
 .../includes/supporting_documents.html        |  22 +--
 .../application_projects/vendor_detail.html   |  47 +++++++
 hypha/apply/projects/urls.py                  |   4 +
 hypha/apply/projects/views/__init__.py        |   4 +-
 hypha/apply/projects/views/vendor.py          | 127 +++++++++++++++++-
 9 files changed, 235 insertions(+), 16 deletions(-)
 create mode 100644 hypha/apply/projects/migrations/0039_add_required_vendor_setup_field_to_project_settings.py
 create mode 100644 hypha/apply/projects/migrations/0040_add_created_and_updated_at.py
 create mode 100644 hypha/apply/projects/templates/application_projects/vendor_detail.html

diff --git a/hypha/apply/projects/migrations/0039_add_required_vendor_setup_field_to_project_settings.py b/hypha/apply/projects/migrations/0039_add_required_vendor_setup_field_to_project_settings.py
new file mode 100644
index 000000000..dadb12e37
--- /dev/null
+++ b/hypha/apply/projects/migrations/0039_add_required_vendor_setup_field_to_project_settings.py
@@ -0,0 +1,18 @@
+# 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),
+        ),
+    ]
diff --git a/hypha/apply/projects/migrations/0040_add_created_and_updated_at.py b/hypha/apply/projects/migrations/0040_add_created_and_updated_at.py
new file mode 100644
index 000000000..557cb6f60
--- /dev/null
+++ b/hypha/apply/projects/migrations/0040_add_created_and_updated_at.py
@@ -0,0 +1,25 @@
+# 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'),
+        ),
+    ]
diff --git a/hypha/apply/projects/models/project.py b/hypha/apply/projects/models/project.py
index 2ff288edf..5bd291fb2 100644
--- a/hypha/apply/projects/models/project.py
+++ b/hypha/apply/projects/models/project.py
@@ -372,6 +372,7 @@ class ProjectApprovalForm(BaseStreamForm, models.Model):
 @register_setting
 class ProjectSettings(BaseSetting):
     compliance_email = models.TextField("Compliance Email")
+    vendor_setup_required = models.BooleanField(default=True)
 
 
 class Approval(models.Model):
diff --git a/hypha/apply/projects/models/vendor.py b/hypha/apply/projects/models/vendor.py
index 887747d1d..e3b84e27f 100644
--- a/hypha/apply/projects/models/vendor.py
+++ b/hypha/apply/projects/models/vendor.py
@@ -45,7 +45,8 @@ class Vendor(models.Model):
         ('organization', _('Yes, the account belongs to the organisation above')),
         ('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(
         settings.AUTH_USER_MODEL, on_delete=models.PROTECT,
         related_name='vendor'
diff --git a/hypha/apply/projects/templates/application_projects/includes/supporting_documents.html b/hypha/apply/projects/templates/application_projects/includes/supporting_documents.html
index 089df603a..2c058cae8 100644
--- a/hypha/apply/projects/templates/application_projects/includes/supporting_documents.html
+++ b/hypha/apply/projects/templates/application_projects/includes/supporting_documents.html
@@ -19,16 +19,18 @@
                 {% endif %}
             </div>
         </li>
-
-        <li class="docs-block__row">
-            <div class="docs-block__row-inner">
-                <svg class="icon docs-block__icon"><use xlink:href="#tick"></use></svg>
-                <p class="docs-block__title">Contractor Setup Form</p>
-            </div>
-            <div class="docs-block__row-inner">
-                <a class="docs-block__link" href="{% url 'apply:projects:vendor' pk=project.pk %}">Update info</a>
-            </div>
-        </li>
+        {% if settings.application_projects.ProjectSettings.vendor_setup_required and project.vendor %}
+            <li class="docs-block__row">
+                <div class="docs-block__row-inner">
+                    <svg class="icon docs-block__icon"><use xlink:href="#tick"></use></svg>
+                    <p class="docs-block__title">Contractor Setup Form</p>
+                </div>
+                <div class="docs-block__row-inner">
+                    <a class="docs-block__link" href="{% url 'apply:projects:vendor-detail' pk=project.pk vendor_pk=project.vendor.pk %}">View</a>
+                    <a class="docs-block__link" href="{% url 'apply:projects:vendor' pk=project.pk %}">Update info</a>
+                </div>
+            </li>
+        {% endif %}
 
         <li class="docs-block__row">
             <div class="docs-block__row-inner">
diff --git a/hypha/apply/projects/templates/application_projects/vendor_detail.html b/hypha/apply/projects/templates/application_projects/vendor_detail.html
new file mode 100644
index 000000000..417e4ce8b
--- /dev/null
+++ b/hypha/apply/projects/templates/application_projects/vendor_detail.html
@@ -0,0 +1,47 @@
+{% 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 %}
diff --git a/hypha/apply/projects/urls.py b/hypha/apply/projects/urls.py
index 9eee94fcb..35b4a9ecc 100644
--- a/hypha/apply/projects/urls.py
+++ b/hypha/apply/projects/urls.py
@@ -21,6 +21,8 @@ from .views import (
     ReportPrivateMedia,
     ReportSkipView,
     ReportUpdateView,
+    VendorDetailView,
+    VendorPrivateMediaView,
 )
 
 app_name = 'projects'
@@ -37,6 +39,8 @@ urlpatterns = [
         path('simplified/', ProjectDetailSimplifiedView.as_view(), name='simplified'),
         path('request/', CreatePaymentRequestView.as_view(), name='request'),
         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('', PaymentRequestListView.as_view(), name='all'),
diff --git a/hypha/apply/projects/views/__init__.py b/hypha/apply/projects/views/__init__.py
index c08899889..61a184d6d 100644
--- a/hypha/apply/projects/views/__init__.py
+++ b/hypha/apply/projects/views/__init__.py
@@ -41,7 +41,7 @@ from .report import (
     ReportSkipView,
     ReportUpdateView,
 )
-from .vendor import CreateVendorView
+from .vendor import CreateVendorView, VendorDetailView, VendorPrivateMediaView
 
 __all__ = [
     'ChangePaymentRequestStatusView',
@@ -82,4 +82,6 @@ __all__ = [
     'ReportFrequencyUpdate',
     'ReportListView',
     'CreateVendorView',
+    'VendorDetailView',
+    'VendorPrivateMediaView',
 ]
diff --git a/hypha/apply/projects/views/vendor.py b/hypha/apply/projects/views/vendor.py
index dd98fd1d2..d6923f7e5 100644
--- a/hypha/apply/projects/views/vendor.py
+++ b/hypha/apply/projects/views/vendor.py
@@ -1,12 +1,20 @@
+from django.contrib.auth.models import User
+from hypha.apply.projects.models.vendor import VendorFormSettings
 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.utils.decorators import method_decorator
 from django.db.models.fields.files import FieldFile
 from django.shortcuts import get_object_or_404, render
 from formtools.wizard.views import SessionWizardView
 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 PrivateMediaView
 
 from ..forms import (
     CreateVendorFormStep1,
@@ -16,7 +24,7 @@ from ..forms import (
     CreateVendorFormStep5,
     CreateVendorFormStep6,
 )
-from ..models import BankInformation, DueDiligenceDocument, Project
+from ..models import BankInformation, DueDiligenceDocument, Project, ProjectSettings, Vendor
 
 
 def show_extra_info_form(wizard):
@@ -28,13 +36,16 @@ def show_extra_info_form(wizard):
 
 class VendorAccessMixin:
     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
         project = self.get_project()
         is_owner = request.user == project.user
         if not (is_owner or is_admin):
             raise PermissionDenied
         if not project.vendor:
-            raise PermissionDenied
+            raise Http404
         return super().dispatch(request, *args, **kwargs)
 
 
@@ -109,6 +120,7 @@ class CreateVendorView(VendorAccessMixin, SessionWizardView):
         vendor.contractor_name = cleaned_data['contractor_name']
         vendor.type = cleaned_data['type']
         vendor.required_to_pay_taxes = cleaned_data['required_to_pay_taxes']
+        vendor.updated_at = timezone.now()
         vendor.save()
 
         not_deleted_original_filenames = [
@@ -176,3 +188,110 @@ class CreateVendorView(VendorAccessMixin, SessionWizardView):
         kwargs = super(CreateVendorView, self).get_form_kwargs(step)
         kwargs['site'] = Site.find_for_request(self.request)
         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
-- 
GitLab