Check in a basic invoice view for organizations

This commit is contained in:
Joseph Schorr 2013-11-13 17:47:45 -05:00
parent b8dc051705
commit e649e669e1
4 changed files with 154 additions and 0 deletions

View file

@ -1252,6 +1252,38 @@ def subscribe(user, plan, token, accepted_plans):
return resp
@app.route('/api/organization/<orgname>/invoices', methods=['GET'])
@api_login_required
def org_invoices_api(orgname):
def invoice_view(i):
return {
'id': i.id,
'date': i.date,
'period_start': i.period_start,
'period_end': i.period_end,
'paid': i.paid,
'amount_due': i.amount_due,
'next_payment_attempt': i.next_payment_attempt,
'attempted': i.attempted,
'closed': i.closed,
'total': i.total,
'plan': i.lines.data[0].plan.id
}
permission = AdministerOrganizationPermission(orgname)
if permission.can():
organization = model.get_organization(orgname)
if not organization.stripe_id:
abort(404)
invoices = stripe.Invoice.all(customer=organization.stripe_id, count=12)
return jsonify({
'invoices': [invoice_view(i) for i in invoices.data]
})
abort(403)
@app.route('/api/organization/<orgname>/plan', methods=['PUT'])
@api_login_required
def subscribe_org_api(orgname):

View file

@ -1638,6 +1638,54 @@ p.editable:hover i {
display: inline-block;
}
.org-admin .invoice-title {
padding: 6px;
cursor: pointer;
}
.org-admin .invoice-status .success {
color: green;
}
.org-admin .invoice-status .pending {
color: steelblue;
}
.org-admin .invoice-status .danger {
color: red;
}
.org-admin .invoice-amount:before {
content: '$';
}
.org-admin .invoice-details {
margin-left: 10px;
margin-bottom: 10px;
padding: 4px;
padding-left: 6px;
border-left: 2px solid #eee !important;
}
.org-admin .invoice-details td {
border: 0px solid transparent !important;
}
.org-admin .invoice-details dl {
margin: 0px;
}
.org-admin .invoice-details dd {
margin-left: 10px;
padding: 6px;
margin-bottom: 10px;
}
.org-admin .invoice-title:hover {
color: steelblue;
}
.org-list h2 {
margin-bottom: 20px;
}

View file

@ -1187,6 +1187,10 @@ function OrgAdminCtrl($rootScope, $scope, Restangular, $routeParams, UserService
// Load the list of plans.
PlanService.getPlans(function(plans) {
$scope.plans = plans.business;
$scope.plan_map = {};
for (var i = 0; i < plans.business.length; ++i) {
$scope.plan_map[plans.business[i].stripeId] = plans.business[i];
}
});
var orgname = $routeParams.orgname;
@ -1194,6 +1198,23 @@ function OrgAdminCtrl($rootScope, $scope, Restangular, $routeParams, UserService
$scope.orgname = orgname;
$scope.membersLoading = true;
$scope.membersFound = null;
$scope.invoiceLoading = true;
$scope.loadInvoices = function() {
if ($scope.invoices) { return; }
$scope.invoiceLoading = true;
var getInvoices = Restangular.one(getRestUrl('organization', orgname, 'invoices'));
getInvoices.get().then(function(resp) {
$scope.invoiceExpanded = {};
$scope.invoices = resp.invoices;
$scope.invoiceLoading = false;
});
};
$scope.toggleInvoice = function(id) {
$scope.invoiceExpanded[id] = !$scope.invoiceExpanded[id];
};
$scope.loadMembers = function() {
if ($scope.membersFound) { return; }

View file

@ -15,6 +15,7 @@
<ul class="nav nav-pills nav-stacked">
<li class="active"><a href="javascript:void(0)" data-toggle="tab" data-target="#plan">Plan and Usage</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#members" ng-click="loadMembers()">Members</a></li>
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#billing" ng-click="loadInvoices()">Billing</a></li>
</ul>
</div>
@ -26,6 +27,58 @@
<div class="plan-manager" organization="orgname"></div>
</div>
<!-- Billing tab -->
<div id="billing" class="tab-pane">
<div ng-show="invoiceLoading">
<i class="fa fa-spinner fa-spin fa-3x"></i>
</div>
<div ng-show="!invoiceLoading && !invoices">
No invoices have been created
</div>
<div ng-show="!invoiceLoading && invoices">
<table class="table">
<thead>
<th>Billing Date/Time</th>
<th>Amount Due</th>
<th>Status</th>
</thead>
<tbody class="invoice" ng-repeat="invoice in invoices">
<tr class="invoice-title" ng-click="toggleInvoice(invoice.id)">
<td><span class="invoice-datetime">{{ invoice.date * 1000 | date:'medium' }}</span></td>
<td><span class="invoice-amount">{{ invoice.amount_due / 100 }}</span></td>
<td>
<span class="invoice-status">
<span class="success" ng-show="invoice.paid">Paid - Thank you!</span>
<span class="danger" ng-show="!invoice.paid && invoice.attempted">Payment failed - Will retry soon</span>
<span class="pending" ng-show="!invoice.paid && !invoice.attempted">Payment pending</span>
</span>
</td>
</tr>
<tr ng-class="invoiceExpanded[invoice.id] ? 'in' : 'out'" class="invoice-details panel-collapse collapse">
<td colspan="3">
<dl class="dl-normal">
<dt>Billing Period</dt>
<dd>
<span>{{ invoice.period_start * 1000 | date:'mediumDate' }}</span> -
<span>{{ invoice.period_end * 1000 | date:'mediumDate' }}</span>
</dd>
<dt>Plan</dt>
<dd>
<span>{{ plan_map[invoice.plan].title }}</span>
<span>{{ plan_map[invoice.plan].price / 100 }}</span>
</dd>
</dl>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Members tab -->
<div id="members" class="tab-pane">
<i class="fa fa-spinner fa-spin fa-3x" ng-show="membersLoading"></i>