forked from uncloud/uncloud
Task #9530 bills that include vat per month in html or pdf format
This commit is contained in:
parent
1c3d3efb3a
commit
1aeb757e22
17 changed files with 298 additions and 39 deletions
|
@ -1325,7 +1325,7 @@ body, html {
|
||||||
}
|
}
|
||||||
|
|
||||||
.transaction-title {
|
.transaction-title {
|
||||||
background-color: #f1f5f6;
|
background-color: #d8e4e6;
|
||||||
border-top: 1px solid #e9eff0;
|
border-top: 1px solid #e9eff0;
|
||||||
border-bottom: 1px solid #e9eff0;
|
border-bottom: 1px solid #e9eff0;
|
||||||
}
|
}
|
||||||
|
@ -1795,7 +1795,7 @@ body, html {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.accordion:not(.accordion-alternate) .card-header a {
|
.accordion:not(.accordion-alternate) .card-header a {
|
||||||
background-color: #e41d25;
|
background-color: #2b343c;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
.accordion:not(.accordion-alternate) .card-header a.collapsed {
|
.accordion:not(.accordion-alternate) .card-header a.collapsed {
|
||||||
|
@ -1826,8 +1826,10 @@ body, html {
|
||||||
}
|
}
|
||||||
.accordion .card-body {
|
.accordion .card-body {
|
||||||
line-height: 26px;
|
line-height: 26px;
|
||||||
padding: 1rem 0 1rem 2.25rem;
|
border-left: 4px solid #2b343c;
|
||||||
|
background-color: #f9f9f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.accordion.arrow-right .card-header a {
|
.accordion.arrow-right .card-header a {
|
||||||
padding-left: 1.25rem;
|
padding-left: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ function fetch_pricing() {
|
||||||
if(data['total'] > balance) {
|
if(data['total'] > balance) {
|
||||||
$('#has-enough-balance').hide();
|
$('#has-enough-balance').hide();
|
||||||
$('#cards-section').show();
|
$('#cards-section').show();
|
||||||
window.cardNumberElement = loadStripe(stripe);
|
window.cardNumberElement = loadStripe(window.stripe);
|
||||||
} else {
|
} else {
|
||||||
$('#cards-section').hide();
|
$('#cards-section').hide();
|
||||||
$('#has-enough-balance').show();
|
$('#has-enough-balance').show();
|
||||||
|
@ -44,7 +44,7 @@ function incrementValue(e) {
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
var stripe = Stripe(window.stripeKey);
|
window.stripe = Stripe(window.stripeKey);
|
||||||
|
|
||||||
if ($('#pricing_name') != undefined) {
|
if ($('#pricing_name') != undefined) {
|
||||||
fetch_pricing();
|
fetch_pricing();
|
||||||
|
@ -56,7 +56,7 @@ $(document).ready(function () {
|
||||||
|
|
||||||
var hasCreditcard = window.hasCreditcard || false;
|
var hasCreditcard = window.hasCreditcard || false;
|
||||||
if (hasCreditcard) {
|
if (hasCreditcard) {
|
||||||
window.cardNumberElement = loadStripe(stripe);
|
window.cardNumberElement = loadStripe(window.stripe);
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitBillingForm(pmId) {
|
function submitBillingForm(pmId) {
|
||||||
|
@ -76,7 +76,7 @@ $(document).ready(function () {
|
||||||
$('#id_payment_method').val(paymentMethod.id);
|
$('#id_payment_method').val(paymentMethod.id);
|
||||||
submitBillingForm(paymentMethod.id);
|
submitBillingForm(paymentMethod.id);
|
||||||
}
|
}
|
||||||
stripe.createPaymentMethod({
|
window.stripe.createPaymentMethod({
|
||||||
type: 'card',
|
type: 'card',
|
||||||
card: window.cardNumberElement,
|
card: window.cardNumberElement,
|
||||||
})
|
})
|
||||||
|
|
196
matrixhosting/templates/matrixhosting/bills.html
Normal file
196
matrixhosting/templates/matrixhosting/bills.html
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
{% extends "matrixhosting/base.html" %}
|
||||||
|
|
||||||
|
{% load static i18n compress mathfilters %}
|
||||||
|
|
||||||
|
{% block title %} Bills {% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="bg-white shadow-md rounded p-4 my-4">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<ul class="nav nav-tabs flex-column" id="myTabVertical" role="tablist">
|
||||||
|
<li class="nav-item"> <a class="nav-link" id="first-tab" href="{% url 'matrixhosting:billing' %}">{% trans "Payment Moves"%}</a> </li>
|
||||||
|
<li class="nav-item"> <a class="nav-link active" id="second-tab" href="{% url 'matrixhosting:bills' %}">{% trans "Bills"%}</a> </li>
|
||||||
|
<li class="nav-item"> <a class="nav-link" id="fourth-tab" href="{% url 'matrixhosting:cards' %}">{% trans "Payment Methods"%}</a> </li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<div class="tab-content" id="myTabContentVertical">
|
||||||
|
<div class="tab-pane fade show active" id="bills" role="tabpanel" aria-labelledby="bills">
|
||||||
|
<div class="">
|
||||||
|
<!-- Filter
|
||||||
|
============================================= -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col mb-2">
|
||||||
|
<form id="filterBills" method="post">
|
||||||
|
<div class="form-row">
|
||||||
|
<!-- Date Range
|
||||||
|
========================= -->
|
||||||
|
<div class="col-sm-6 col-md-6 form-group">
|
||||||
|
<h3 class="text-5 font-weight-400 d-flex align-items-center px-4 mb-4">{% trans "Bills"%}</h3>
|
||||||
|
<!-- <input id="dateRange" type="text" class="form-control" placeholder="Date Range">
|
||||||
|
<span class="icon-inside"><i class="fas fa-calendar-alt"></i></span> -->
|
||||||
|
</div>
|
||||||
|
<div class="col col-sm-6 text-right text-2">
|
||||||
|
<div class="featured-box float-right style-3">
|
||||||
|
<div class="featured-box-icon text-9 text-light"> <i class="fas fa-wallet"></i> </div>
|
||||||
|
<h3 class="text-6 font-weight-400 text-dark">{{balance}} CHF</h3>
|
||||||
|
<p class="text-muted text-3 opacity-8">{% trans "Available Balance"%}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Filter End -->
|
||||||
|
|
||||||
|
<!-- All Transactions
|
||||||
|
============================================= -->
|
||||||
|
<div class="px-2 py-4 mb-4">
|
||||||
|
<!-- Title
|
||||||
|
=============================== -->
|
||||||
|
<div class="transaction-title mb-1 py-2">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-3 col-sm-3 text-center"><span class="">{% trans "Bill No."%}</span></div>
|
||||||
|
<div class="col-2 col-sm-2 text-center"><span class="">{% trans "Creation Date"%}</span></div>
|
||||||
|
<div class="col-2 col-sm-2 text-center">{% trans "Amount"%}</div>
|
||||||
|
<div class="col-2 col-sm-2 text-center">{% trans "Due Date"%}</div>
|
||||||
|
<div class="col-1 col-sm-1 text-center">{% trans "Closed"%}</div>
|
||||||
|
<div class="col-2 col-sm-2 text-center">{% trans "Download"%}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Title End -->
|
||||||
|
|
||||||
|
<!-- Transaction List
|
||||||
|
=============================== -->
|
||||||
|
<div class="bills-list">
|
||||||
|
<div class="accordion" id="accordionDefault">
|
||||||
|
{% for bill in object_list %}
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header" id="heading{{bill.id}}">
|
||||||
|
<a href="#" data-toggle="collapse" data-target="#collapse{{bill.id}}" aria-expanded="false" aria-controls="collapse{{bill.id}}" class="collapsed">
|
||||||
|
<div class="row align-items-center flex-row">
|
||||||
|
<div class="col-3 col-sm-3 text-center"> <span class="text-2 font-weight-400">#{{bill.id}}</span></div>
|
||||||
|
<div class="col-2 col-sm-2 text-center"> <span class="text-2 font-weight-300">{{bill.creation_date|date:"Y-m-d"}}</span></div>
|
||||||
|
<div class="col-2 col-sm-2 text-center"><span class="text-2 font-weight-300">{{bill.sum}}</span><span class="text-1 text-uppercase"> {{bill.currency}}</span></div>
|
||||||
|
<div class="col-2 col-sm-2 text-center"> <span class="text-2 font-weight-300">{{bill.due_date|date:"Y-m-d"}}</span> </div>
|
||||||
|
<div class="col-1 col-sm-1 text-center text-3">
|
||||||
|
{% if bill.is_closed %}
|
||||||
|
<span class="text-success" data-toggle="tooltip" data-original-title="Closed or Paid"><i class="fas fa-check-circle"></i></span>
|
||||||
|
{%else%}
|
||||||
|
<span class="text-danger" data-toggle="tooltip" data-original-title="Pending"><i class="text-danger fas fa-times-circle"></i></span>
|
||||||
|
{%endif%}
|
||||||
|
</div>
|
||||||
|
<div class="col-2 col-sm-2 text-center text-3">
|
||||||
|
<form method="get" action="{% url 'matrix:invoice_download' bill_id=bill.id %}">
|
||||||
|
<button class="download-bill border border-primary" type="submit"><span class="text-primary" data-toggle="tooltip" data-original-title="Download"><i class="text-primary fas fa-file-download"></i></span></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div id="collapse{{bill.id}}" class="collapse" aria-labelledby="{{bill.id}}" data-parent="#accordionDefault">
|
||||||
|
<div class="mt-n1 card-body">
|
||||||
|
<h6>Bill Lines:</h6>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="bg-white table table-bordered text-1">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Order</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Start Date</th>
|
||||||
|
<th>End Date</th>
|
||||||
|
<th>Subtotal</th>
|
||||||
|
<th>Total</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for record in bill.bill_records.all %}
|
||||||
|
<tr>
|
||||||
|
<td>#{{record.order.id}}</td>
|
||||||
|
<td>{{record.description}}</td>
|
||||||
|
<td>{{record.starting_date|date:"Y-m-d"}}</td>
|
||||||
|
<td>{{record.ending_date|date:"Y-m-d"}}</td>
|
||||||
|
<td>{{record.subtotal}}</td>
|
||||||
|
<td>{{record.sum}}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6"></div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div class="row text-1 text-right">
|
||||||
|
<div class="col-md-6 col-sm-6 col-xs-6">
|
||||||
|
<p><strong>{% trans "Subtotal" %}</strong></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-sm-6 col-xs-6">
|
||||||
|
<p><strong class="pull-right">{{bill.subtotal}}</strong></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row text-right">
|
||||||
|
<div class="col-md-6 col-sm-6 col-xs-6">
|
||||||
|
<p><span>{{bill.billing_address.get_country_display}} VAT {{ bill.vat_rate|mul:100 }}%</span></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-sm-6 col-xs-6">
|
||||||
|
<p><span class="pull-right" >{{bill.vat_amount}}</span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6"></div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div class="row text-1 text-right">
|
||||||
|
<div class="col-md-6 col-sm-6 col-xs-6">
|
||||||
|
<p><strong>{% trans "Total" %}</strong></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-sm-6 col-xs-6">
|
||||||
|
<p><strong class="pull-right">{{bill.sum|floatformat:2}} CHF</strong></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{%endfor%}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Transaction List End -->
|
||||||
|
|
||||||
|
<!-- Pagination will handled later
|
||||||
|
============================================= -->
|
||||||
|
<ul class="pagination justify-content-center mt-4 mb-0" style="display: none;">
|
||||||
|
<li class="page-item disabled"> <a class="page-link" href="#" tabindex="-1"><i class="fas fa-angle-left"></i></a> </li>
|
||||||
|
<li class="page-item"><a class="page-link" href="#">1</a></li>
|
||||||
|
<li class="page-item active"> <a class="page-link" href="#">2 <span class="sr-only">(current)</span></a> </li>
|
||||||
|
<li class="page-item"><a class="page-link" href="#">3</a></li>
|
||||||
|
<li class="page-item d-flex align-content-center flex-wrap text-muted text-5 mx-1">......</li>
|
||||||
|
<li class="page-item"><a class="page-link" href="#">15</a></li>
|
||||||
|
<li class="page-item"> <a class="page-link" href="#"><i class="fas fa-angle-right"></i></a> </li>
|
||||||
|
</ul>
|
||||||
|
<!-- Paginations end -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- All Transactions End -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block js_extra %}
|
||||||
|
<script>
|
||||||
|
$('.download-bill').click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
$(e.target).closest("form").submit();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock js_extra %}
|
|
@ -2,16 +2,17 @@
|
||||||
|
|
||||||
{% load static i18n compress %}
|
{% load static i18n compress %}
|
||||||
|
|
||||||
{% block title %} Payments {% endblock %}
|
{% block title %} Payment Methods {% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="bg-white shadow-md rounded p-4 m-4">
|
<div class="bg-white shadow-md rounded p-4 my-4">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<ul class="nav nav-tabs flex-column" id="myTabVertical" role="tablist">
|
<ul class="nav nav-tabs flex-column" id="myTabVertical" role="tablist">
|
||||||
<li class="nav-item"> <a class="nav-link" id="first-tab" href="{% url 'matrixhosting:billing' %}">Payment Moves</a> </li>
|
<li class="nav-item"> <a class="nav-link" id="first-tab" href="{% url 'matrixhosting:billing' %}">{% trans "Payment Moves"%}</a> </li>
|
||||||
<li class="nav-item"> <a class="nav-link active" id="fourth-tab" href="{% url 'matrixhosting:cards' %}">Payment Methods</a> </li>
|
<li class="nav-item"> <a class="nav-link" id="second-tab" href="{% url 'matrixhosting:bills' %}">{% trans "Bills"%}</a> </li>
|
||||||
|
<li class="nav-item"> <a class="nav-link active" id="fourth-tab" href="{% url 'matrixhosting:cards' %}">{% trans "Payment Methods"%}</a> </li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
|
@ -23,8 +24,8 @@
|
||||||
<div class="col-sm-12 col-lg-12">
|
<div class="col-sm-12 col-lg-12">
|
||||||
<div class="featured-box style-3 float-right mb-4">
|
<div class="featured-box style-3 float-right mb-4">
|
||||||
<div class="featured-box-icon text-17 text-light"> <i class="fas fa-wallet"></i> </div>
|
<div class="featured-box-icon text-17 text-light"> <i class="fas fa-wallet"></i> </div>
|
||||||
<h3 class="text-9 font-weight-400">{{balance}} CHF</h3>
|
<h3 class="text-7 font-weight-400">{{balance}} CHF</h3>
|
||||||
<a href="" data-target="#make-deposit" data-toggle="modal">Make a deposit</a>
|
<a href="" data-target="#make-deposit" data-toggle="modal">{% trans "Make a deposit"%}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,7 +35,6 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h3 class="text-4 font-weight-400 mb-4">{% trans "Credit or Debit Cards"%} <span class="text-muted text-3">({% trans "for payments"%})</span></h3>
|
<h3 class="text-4 font-weight-400 mb-4">{% trans "Credit or Debit Cards"%} <span class="text-muted text-3">({% trans "for payments"%})</span></h3>
|
||||||
<hr class="mt-0 mb-2 mx-n8">
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% for card in object_list %}
|
{% for card in object_list %}
|
||||||
<div class="col-12 col-sm-6 col-lg-4 mt-2">
|
<div class="col-12 col-sm-6 col-lg-4 mt-2">
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
thru<br>
|
thru<br>
|
||||||
</span> <span class="text-4 opacity-9">{{card.expiry_date|date:"m"}}/{{card.expiry_date|date:"y"}}</span> {% if card.active %}<span class="badge badge-warning text-0 font-weight-500 rounded-pill px-2 ml-auto">{% trans "Primary"%}</span>{%endif%} </p>
|
</span> <span class="text-4 opacity-9">{{card.expiry_date|date:"m"}}/{{card.expiry_date|date:"y"}}</span> {% if card.active %}<span class="badge badge-warning text-0 font-weight-500 rounded-pill px-2 ml-auto">{% trans "Primary"%}</span>{%endif%} </p>
|
||||||
<p class="d-flex align-items-center m-0"> <span class="text-uppercase font-weight-500">{{card.card_name}}</span> <img class="ml-auto" src="{% static 'matrixhosting/images/' %}{{card.brand}}.png" alt="visa" title=""> </p>
|
<p class="d-flex align-items-center m-0"> <span class="text-uppercase font-weight-500">{{card.card_name}}</span> <img class="ml-auto" src="{% static 'matrixhosting/images/' %}{{card.brand}}.png" alt="visa" title=""> </p>
|
||||||
<div class="account-card-overlay rounded" data-card="{{card.card_id}}"> <a href="#" data-href="{% url 'payments:card_activate'%}" class="activate-btn text-light btn-link mx-2"><span class="mr-1"><i class="fas fa-edit"></i></span>{% trans "Set As Primary"%}</a> <a href="#" data-href="{% url 'stripecreditcard-detail' card.id %}" class="delete-card text-light btn-link mx-2"><span class="mr-1"><i class="fas fa-minus-circle"></i></span>{% trans "Delete"%}</a> </div>
|
<div class="account-card-overlay rounded" data-card="{{card.id}}"> <a href="#" data-href="{% url 'payments:card_activate'%}" class="activate-btn text-light btn-link mx-2"><span class="mr-1"><i class="fas fa-edit"></i></span>{% trans "Set As Primary"%}</a> <a href="#" data-href="{% url 'stripecreditcard-detail' card.id %}" class="delete-card text-light btn-link mx-2"><span class="mr-1"><i class="fas fa-minus-circle"></i></span>{% trans "Delete"%}</a> </div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
<!-- Primary Navigation
|
<!-- Primary Navigation
|
||||||
============================== -->
|
============================== -->
|
||||||
{% url 'matrix:index' as index_url %}
|
{% url 'matrix:index' as index_url %}
|
||||||
|
{% url 'matrix:orders' as orders_url %}
|
||||||
{% url 'matrix:billing' as payments_url %}
|
{% url 'matrix:billing' as payments_url %}
|
||||||
<nav class="primary-menu navbar navbar-expand-lg">
|
<nav class="primary-menu navbar navbar-expand-lg">
|
||||||
<div id="header-nav" class="collapse navbar-collapse">
|
<div id="header-nav" class="collapse navbar-collapse">
|
||||||
|
@ -26,8 +27,8 @@
|
||||||
<li><a href="">Pricing</a></li>
|
<li><a href="">Pricing</a></li>
|
||||||
<li><a href="">Contact Us</a></li>
|
<li><a href="">Contact Us</a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li><a href=>Dashboard</a></li>
|
<li><a class="{% if request.path == orders_url %}active{%endif%}" href="{{orders_url}}">Orders</a></li>
|
||||||
<li class="{% if request.path == payments_url %}active{%endif%}"><a href="{{payments_url}}">{%trans "Payments" %}</a></li>
|
<li class="{% if request.path == payments_url %}active{%endif%}"><a href="{{payments_url}}">{%trans "Billing" %}</a></li>
|
||||||
<li><a href="">Help</a></li>
|
<li><a href="">Help</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -66,7 +67,6 @@
|
||||||
<li class="text-center text-3 py-2">Hi, {{request.user.username}}</li>
|
<li class="text-center text-3 py-2">Hi, {{request.user.username}}</li>
|
||||||
<li class="dropdown-divider mx-n3"></li>
|
<li class="dropdown-divider mx-n3"></li>
|
||||||
<li><a class="dropdown-item" href=""><i class="fas fa-user"></i>{%trans "My Profile" %}</a></li>
|
<li><a class="dropdown-item" href=""><i class="fas fa-user"></i>{%trans "My Profile" %}</a></li>
|
||||||
<li><a class="dropdown-item" href=""><i class="fas fa-tachometer-alt"></i>{%trans "Dashboard" %}</a></li>
|
|
||||||
<li><a class="dropdown-item" href="{% url 'matrix:orders' %}"><i class="fas fa-shopping-cart"></i>{%trans "Orders" %}</a></li>
|
<li><a class="dropdown-item" href="{% url 'matrix:orders' %}"><i class="fas fa-shopping-cart"></i>{%trans "Orders" %}</a></li>
|
||||||
<li><a class="dropdown-item" href="{% url 'matrix:billing' %}"><i class="fas fa-file-invoice"></i>{%trans "Billing" %}</a></li>
|
<li><a class="dropdown-item" href="{% url 'matrix:billing' %}"><i class="fas fa-file-invoice"></i>{%trans "Billing" %}</a></li>
|
||||||
<li><a class="dropdown-item" href="{% url 'matrix:instances' %}"><i class="fas fa-cloud"></i>{%trans "Instances" %}</a></li>
|
<li><a class="dropdown-item" href="{% url 'matrix:instances' %}"><i class="fas fa-cloud"></i>{%trans "Instances" %}</a></li>
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
<div class="d4" style="margin-top:15px;">
|
<div class="d4" style="margin-top:15px;">
|
||||||
<div class="bold">
|
<div class="bold">
|
||||||
<div class="b1">
|
<div class="b1">
|
||||||
<span>{%trans "Invoice Number:" %}</span>
|
<span>{%trans "Bill Number:" %}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="b2">
|
<div class="b2">
|
||||||
<span>#{{bill.id}}</span>
|
<span>#{{bill.id}}</span>
|
||||||
|
|
|
@ -200,7 +200,7 @@
|
||||||
<p>{% trans "VAT" %}<span class="float-right" id="vat"> {{request.session.pricing.vat_amount}} CHF</span></p>
|
<p>{% trans "VAT" %}<span class="float-right" id="vat"> {{request.session.pricing.vat_amount}} CHF</span></p>
|
||||||
<p class="text-4 font-weight-500">{% trans "Total To Pay"%}
|
<p class="text-4 font-weight-500">{% trans "Total To Pay"%}
|
||||||
<small>
|
<small>
|
||||||
({% if matrix_vm_pricing.vat_inclusive %}{%trans "including VAT" %}{% else %}{%trans "excluding VAT" %}{% endif %})
|
{% if matrix_vm_pricing.vat_inclusive %}({%trans "including VAT" %}){% endif %}
|
||||||
</small>
|
</small>
|
||||||
<span id="total" class="float-right">{{request.session.pricing.total}} CHF</span>
|
<span id="total" class="float-right">{{request.session.pricing.total}} CHF</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -6,12 +6,13 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="bg-white shadow-md rounded p-4 m-4">
|
<div class="bg-white shadow-md rounded p-4 my-4">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<ul class="nav nav-tabs flex-column" id="myTabVertical" role="tablist">
|
<ul class="nav nav-tabs flex-column" id="myTabVertical" role="tablist">
|
||||||
<li class="nav-item"> <a class="nav-link active" id="first-tab" href="{% url 'matrixhosting:billing' %}">Payment Moves</a> </li>
|
<li class="nav-item"> <a class="nav-link active" id="first-tab" href="{% url 'matrixhosting:billing' %}">{% trans "Payment Moves"%}</a> </li>
|
||||||
<li class="nav-item"> <a class="nav-link" id="fourth-tab" href="{% url 'matrixhosting:cards' %}">Payment Methods</a> </li>
|
<li class="nav-item"> <a class="nav-link" id="second-tab" href="{% url 'matrixhosting:bills' %}">{% trans "Bills"%}</a> </li>
|
||||||
|
<li class="nav-item"> <a class="nav-link" id="fourth-tab" href="{% url 'matrixhosting:cards' %}">{% trans "Payment Methods"%}</a> </li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
|
|
|
@ -10,9 +10,10 @@ urlpatterns = [
|
||||||
path('order/new/', OrderPaymentView.as_view(), name='payment'),
|
path('order/new/', OrderPaymentView.as_view(), name='payment'),
|
||||||
path('order/confirm/', OrderConfirmationView.as_view(), name='order_confirmation'),
|
path('order/confirm/', OrderConfirmationView.as_view(), name='order_confirmation'),
|
||||||
path('order/success/', OrderSuccessView.as_view(), name='order_success'),
|
path('order/success/', OrderSuccessView.as_view(), name='order_success'),
|
||||||
path('order/invoice/download', InvoiceDownloadView.as_view(), name='invoice_download'),
|
path('order/invoice/<int:bill_id>/download/', InvoiceDownloadView.as_view(), name='invoice_download'),
|
||||||
path('billing/', PaymentsView.as_view(), name='billing'),
|
path('billing/', PaymentsView.as_view(), name='billing'),
|
||||||
path('billing/cards', CardsView.as_view(), name='cards'),
|
path('billing/cards', CardsView.as_view(), name='cards'),
|
||||||
|
path('billing/bills', BillsView.as_view(), name='bills'),
|
||||||
path('instances/', InstancesView.as_view(), name='instances'),
|
path('instances/', InstancesView.as_view(), name='instances'),
|
||||||
path('orders/', OrdersView.as_view(), name='orders'),
|
path('orders/', OrdersView.as_view(), name='orders'),
|
||||||
path('', IndexView.as_view(), name='index'),
|
path('', IndexView.as_view(), name='index'),
|
||||||
|
|
|
@ -260,7 +260,6 @@ class OrderSuccessView(DetailView):
|
||||||
|
|
||||||
class InvoiceDownloadView(View):
|
class InvoiceDownloadView(View):
|
||||||
template = 'matrixhosting/invoice.html'
|
template = 'matrixhosting/invoice.html'
|
||||||
filename = 'invoice.pdf'
|
|
||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(login_required)
|
||||||
def dispatch(self, *args, **kwargs):
|
def dispatch(self, *args, **kwargs):
|
||||||
|
@ -268,21 +267,22 @@ class InvoiceDownloadView(View):
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = {'base_url': f'{self.request.scheme}://{self.request.get_host()}'}
|
context = {'base_url': f'{self.request.scheme}://{self.request.get_host()}'}
|
||||||
bill = Bill.objects.get(id=self.request.session.get('bill_id'))
|
return context
|
||||||
|
|
||||||
|
def get(self, request, bill_id):
|
||||||
|
cmd_options = settings.REPORT_FORMAT
|
||||||
|
context = self.get_context_data()
|
||||||
|
bill = Bill.objects.get(owner=self.request.user, id=bill_id)
|
||||||
if bill:
|
if bill:
|
||||||
context['bill'] = bill
|
context['bill'] = bill
|
||||||
context['vat_rate'] = str(round(bill.vat_rate * 100, 2))
|
context['vat_rate'] = str(round(bill.vat_rate * 100, 2))
|
||||||
context['tax_amount'] = round(bill.vat_rate * bill.subtotal, 2)
|
context['tax_amount'] = round(bill.vat_rate * bill.subtotal, 2)
|
||||||
return context
|
|
||||||
|
|
||||||
def get(self, request):
|
|
||||||
cmd_options = settings.REPORT_FORMAT
|
|
||||||
return PDFTemplateResponse(request=request,
|
return PDFTemplateResponse(request=request,
|
||||||
template=self.template,
|
template=self.template,
|
||||||
filename = self.filename,
|
filename = f"bill-{bill_id}.pdf",
|
||||||
cmd_options= cmd_options,
|
cmd_options= cmd_options,
|
||||||
footer_template= 'matrixhosting/includes/invoice_footer.html',
|
footer_template= 'matrixhosting/includes/invoice_footer.html',
|
||||||
context= self.get_context_data())
|
context= context)
|
||||||
|
|
||||||
|
|
||||||
class OrdersView(ListView):
|
class OrdersView(ListView):
|
||||||
|
@ -360,4 +360,24 @@ class CardsView(ListView):
|
||||||
uncloud_stripe.sync_cards_for_user(self.request.user)
|
uncloud_stripe.sync_cards_for_user(self.request.user)
|
||||||
return StripeCreditCard.objects.filter(owner=self.request.user).order_by('-active')
|
return StripeCreditCard.objects.filter(owner=self.request.user).order_by('-active')
|
||||||
|
|
||||||
|
class BillsView(ListView):
|
||||||
|
template_name = "matrixhosting/bills.html"
|
||||||
|
model = Bill
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
def dispatch(self, *args, **kwargs):
|
||||||
|
return super().dispatch(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(BillsView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context.update({
|
||||||
|
'balance': get_balance_for_user(self.request.user),
|
||||||
|
})
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Bill.objects.filter(owner=self.request.user).order_by('-creation_date')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ Django==3.2.4
|
||||||
djangorestframework
|
djangorestframework
|
||||||
django-auth-ldap
|
django-auth-ldap
|
||||||
fontawesome-free
|
fontawesome-free
|
||||||
|
django-mathfilters
|
||||||
|
|
||||||
psycopg2
|
psycopg2
|
||||||
ldap3
|
ldap3
|
||||||
|
|
|
@ -16,4 +16,4 @@ GITLAB_PROJECT_ID=
|
||||||
GITLAB_OAUTH_TOKEN=
|
GITLAB_OAUTH_TOKEN=
|
||||||
GITLAB_AUTHOR_EMAIL=
|
GITLAB_AUTHOR_EMAIL=
|
||||||
GITLAB_AUTHOR_NAME=
|
GITLAB_AUTHOR_NAME=
|
||||||
WKHTMLTOPDF_CMD=/usr/bin/wkhtmltopdf
|
WKHTMLTOPDF_CMD=/usr/local/bin/wkhtmltopdf
|
|
@ -62,6 +62,7 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.sites',
|
'django.contrib.sites',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'django_extensions',
|
'django_extensions',
|
||||||
|
'mathfilters',
|
||||||
'compressor',
|
'compressor',
|
||||||
'wkhtmltopdf',
|
'wkhtmltopdf',
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 3.2.4 on 2021-08-12 16:00
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('uncloud_pay', '0026_remove_order_description'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='stripecreditcard',
|
||||||
|
name='card_name',
|
||||||
|
field=models.CharField(default='', max_length=128),
|
||||||
|
),
|
||||||
|
]
|
18
uncloud_pay/migrations/0028_bill_currency.py
Normal file
18
uncloud_pay/migrations/0028_bill_currency.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 3.2.4 on 2021-08-12 16:06
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('uncloud_pay', '0027_alter_stripecreditcard_card_name'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='bill',
|
||||||
|
name='currency',
|
||||||
|
field=models.CharField(choices=[('CHF', 'Swiss Franc')], default='CHF', max_length=32),
|
||||||
|
),
|
||||||
|
]
|
|
@ -998,6 +998,8 @@ class Bill(models.Model):
|
||||||
editable=True,
|
editable=True,
|
||||||
null=False)
|
null=False)
|
||||||
|
|
||||||
|
currency = models.CharField(max_length=32, choices=Currency.choices, default=Currency.CHF)
|
||||||
|
|
||||||
# FIXME: editable=True -> is in the admin, but also editable in DRF
|
# FIXME: editable=True -> is in the admin, but also editable in DRF
|
||||||
# Maybe filter fields in the serializer?
|
# Maybe filter fields in the serializer?
|
||||||
|
|
||||||
|
@ -1030,6 +1032,10 @@ class Bill(models.Model):
|
||||||
bill_records = BillRecord.objects.filter(bill=self)
|
bill_records = BillRecord.objects.filter(bill=self)
|
||||||
return sum([ br.subtotal for br in bill_records ])
|
return sum([ br.subtotal for br in bill_records ])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vat_amount(self):
|
||||||
|
return round(self.vat_rate * self.subtotal, 2)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def vat_rate(self):
|
def vat_rate(self):
|
||||||
return VATRate.get_vat_rate(self.billing_address, when=self.ending_date)
|
return VATRate.get_vat_rate(self.billing_address, when=self.ending_date)
|
||||||
|
@ -1086,8 +1092,6 @@ class Bill(models.Model):
|
||||||
Create the next bill for a specific order of a user
|
Create the next bill for a specific order of a user
|
||||||
"""
|
"""
|
||||||
bill = cls.get_or_create_bill(order.billing_address, ending_date=ending_date)
|
bill = cls.get_or_create_bill(order.billing_address, ending_date=ending_date)
|
||||||
print("??????????????????????????????")
|
|
||||||
print(bill.id)
|
|
||||||
order.create_bill_record(bill)
|
order.create_bill_record(bill)
|
||||||
return bill
|
return bill
|
||||||
|
|
||||||
|
@ -1206,9 +1210,6 @@ class BillRecord(models.Model):
|
||||||
return bill_line
|
return bill_line
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
|
|
||||||
print(self.ending_date)
|
|
||||||
print(self.starting_date)
|
|
||||||
if self.ending_date < self.starting_date:
|
if self.ending_date < self.starting_date:
|
||||||
raise ValidationError("End date cannot be before starting date")
|
raise ValidationError("End date cannot be before starting date")
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ class CardActivateView(View):
|
||||||
def post(self, request, **args):
|
def post(self, request, **args):
|
||||||
card_id = request.POST.get('card_id')
|
card_id = request.POST.get('card_id')
|
||||||
if card_id:
|
if card_id:
|
||||||
matched_card = StripeCreditCard.objects.filter(owner=self.request.user, card_id=card_id).first()
|
matched_card = StripeCreditCard.objects.filter(owner=self.request.user, id=card_id).first()
|
||||||
matched_card.activate()
|
matched_card.activate()
|
||||||
return JsonResponse({'success': 1})
|
return JsonResponse({'success': 1})
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Reference in a new issue