Merge branch 'xero' of gitlab.com:pitmutt/zgo into xero
This commit is contained in:
commit
5f05de246d
17 changed files with 454 additions and 58 deletions
BIN
AccountingIntegration.odt
Normal file
BIN
AccountingIntegration.odt
Normal file
Binary file not shown.
BIN
AccountingIntegration.pdf
Normal file
BIN
AccountingIntegration.pdf
Normal file
Binary file not shown.
|
@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
- First testing version of pmtservice for Xero accounting is running
|
||||
|
||||
### Added
|
||||
|
||||
- Added new service for Xero integration
|
||||
|
|
BIN
CurrencyCodes.ods
Normal file
BIN
CurrencyCodes.ods
Normal file
Binary file not shown.
|
@ -1,6 +1,6 @@
|
|||
<mat-toolbar color="primary">
|
||||
<span align="center">
|
||||
<img class="logo" src="/assets/logo-new-white.png" height="40px" />
|
||||
<img class="logo" src="/assets/logo-new-white_01.png" height="40px" />
|
||||
</span>
|
||||
<span class="spacer"></span>
|
||||
<span align="center">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div align="center" class="text">
|
||||
<mat-card class="coolcard">
|
||||
<img src="/assets/logo-new-white.png" height="120px" />
|
||||
<img src="/assets/logo-new-white_01.png" height="120px" />
|
||||
<p class="text">Last block seen: <span class="numbers">{{ heightUpdate | async }}</span></p>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
.invoice {
|
||||
font-family: Roboto Mono !important;
|
||||
}
|
||||
|
||||
.zecSign {
|
||||
margin-bottom: -4px;
|
||||
font-size: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.invoiceHeader {
|
||||
display: flex;
|
||||
font-family: Spartan;
|
||||
font-weight: 700;
|
||||
font-size: 26px;
|
||||
color: white;
|
||||
justify-content: space-between;
|
||||
line-height: 40px;
|
||||
padding: 10px;
|
||||
vertical-align: center;
|
||||
max-width: 600px;
|
||||
background: #ff5722;
|
||||
}
|
||||
|
|
@ -1 +1,163 @@
|
|||
<p>{{ pmtData.ownerId }}</p>
|
||||
<div style="font-family: 'Spartan';
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;">
|
||||
<div class="container">
|
||||
<div class="invoiceHeader">
|
||||
<img class="logo" src="/assets/logo-new-white.png" height="40px" />
|
||||
</div>
|
||||
<div *ngIf="reportType==1">
|
||||
<div style="height: 50px;">
|
||||
</div>
|
||||
<div style="font-weight: 700;
|
||||
font-size: 25px;
|
||||
text-align: center;">
|
||||
Invalid Owner ID!!
|
||||
</div>
|
||||
<div style="height: 40px;">
|
||||
</div>
|
||||
Payment request was not processed!!
|
||||
<div style="height: 20px;">
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="reportType==2">
|
||||
<div style="height: 50px;">
|
||||
</div>
|
||||
<div style="font-weight: 700;
|
||||
font-size: 25px;
|
||||
text-align: center;">
|
||||
Payment service not<br>
|
||||
enabled for<br>
|
||||
{{ owner.name}}
|
||||
</div>
|
||||
<div style="height: 40px;">
|
||||
</div>
|
||||
Payment request was not processed!!
|
||||
<div style="height: 20px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="reportType==3">
|
||||
<div style="height: 50px;">
|
||||
</div>
|
||||
<div style="font-weight: 700;
|
||||
font-size: 25px;
|
||||
text-align: center;">
|
||||
Connection to Xero<br>server failed!!
|
||||
</div>
|
||||
<div style="height: 40px;">
|
||||
</div>
|
||||
Payment request was not processed!!
|
||||
<div style="height: 20px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="reportType==4">
|
||||
<div style="height: 50px;">
|
||||
</div>
|
||||
<div style="font-weight: 700;
|
||||
font-size: 25px;
|
||||
text-align: center;">
|
||||
Invoice<br>{{ pmtData.invoice }}<br>not found!!
|
||||
</div>
|
||||
<div style="height: 40px;">
|
||||
</div>
|
||||
Payment request was not processed!!
|
||||
<div style="height: 20px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="reportType==5">
|
||||
<div style="height: 50px;">
|
||||
</div>
|
||||
<div style="font-weight: 700;
|
||||
font-size: 25px;
|
||||
text-align: center;">
|
||||
Invoice<br>{{ pmtData.invoice }}<br>type invalid!!
|
||||
</div>
|
||||
<div style="height: 40px;">
|
||||
</div>
|
||||
Payment request was not processed!!
|
||||
<div style="height: 20px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="reportType==6">
|
||||
<div style="height: 50px;">
|
||||
</div>
|
||||
<div style="font-weight: 700;
|
||||
font-size: 25px;
|
||||
text-align: center;">
|
||||
Invoice <br>{{ pmtData.invoice }}<br>already paid!!
|
||||
</div>
|
||||
<div style="height: 40px;">
|
||||
</div>
|
||||
Payment request was not processed!!
|
||||
<div style="height: 20px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="reportType==7">
|
||||
<div style="height: 50px;">
|
||||
</div>
|
||||
<div style="font-weight: 700;
|
||||
font-size: 25px;
|
||||
text-align: center;">
|
||||
Currency <br>[ {{ pmtData.currency }} ]<br>not supported!!
|
||||
</div>
|
||||
<div style="height: 40px;">
|
||||
</div>
|
||||
Payment request was not processed!!
|
||||
<div style="height: 20px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="reportType==8">
|
||||
<div style="height: 50px;">
|
||||
</div>
|
||||
<div style="font-weight: 700;
|
||||
font-size: 25px;
|
||||
text-align: center;">
|
||||
Amount does not<br>
|
||||
match value<br>
|
||||
reported by Xero!!
|
||||
</div>
|
||||
<div style="height: 40px;">
|
||||
</div>
|
||||
Payment request was not processed!!
|
||||
<div style="height: 20px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="reportType==0">
|
||||
<div style="height: 50px;">
|
||||
</div>
|
||||
<div style="font-weight: 700;
|
||||
font-size: 25px;
|
||||
text-align: center;">
|
||||
Invoice Goes here!!!
|
||||
</div>
|
||||
<div style="height: 40px;">
|
||||
</div>
|
||||
Payment request was not processed!!
|
||||
<div style="height: 20px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<p>Owner = {{ pmtData.ownerId }}</p>
|
||||
<p>Owner ID = xxxx</p>
|
||||
<p>Type = {{ inv_Type }}</p>
|
||||
<p>Invoice ID = {{ inv_Id }}</p>
|
||||
<p>Invoice No = {{ inv_No }}</p>
|
||||
<p>Contact = {{ inv_Contact }}</p>
|
||||
<p>Currency = {{ inv_Currency }}</p>
|
||||
<p>Total = {{ inv_Total }}</p>
|
||||
<p>Date = {{ inv_Date }}</p>
|
||||
<p>Status = {{ inv_Status }}</p>
|
||||
<p>Short Code = {{ invData.inv_shortCode }}</p>
|
||||
<p>Process Date = {{ invData.inv_ProcDate }}</p>
|
||||
-->
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -1,6 +1,14 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router, ActivatedRoute, Params } from "@angular/router";
|
||||
import { PmtData } from "./pmtservice.model"
|
||||
import { HttpClient, HttpParams, HttpHeaders } from "@angular/common/http";
|
||||
import { PmtData } from "./pmtservice.model";
|
||||
import { XeroInvoice } from "./xeroinvoice.model";
|
||||
import { Owner } from '../owner.model';
|
||||
import { ConfigData } from '../configdata';
|
||||
import { Item } from '../items/item.model'
|
||||
|
||||
var Buffer = require('buffer/').Buffer;
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-pmtservice',
|
||||
|
@ -10,6 +18,8 @@ import { PmtData } from "./pmtservice.model"
|
|||
|
||||
export class PmtserviceComponent implements OnInit {
|
||||
|
||||
beUrl = ConfigData.Be_URL;
|
||||
private reqHeaders: HttpHeaders = new HttpHeaders();
|
||||
|
||||
public pmtData : PmtData = {
|
||||
ownerId :'',
|
||||
|
@ -19,18 +29,174 @@ export class PmtserviceComponent implements OnInit {
|
|||
shortcode: ''
|
||||
};
|
||||
|
||||
constructor(private activatedRoute: ActivatedRoute) {}
|
||||
public invData : XeroInvoice = {
|
||||
inv_Type : '',
|
||||
inv_Id : '',
|
||||
inv_No : '',
|
||||
inv_Contact : '',
|
||||
inv_Currency : '',
|
||||
inv_CurrencyRate : 0,
|
||||
inv_Status : '',
|
||||
inv_Total : 0,
|
||||
inv_Date : new Date(),
|
||||
inv_shortCode : '',
|
||||
inv_ProcDate : new Date()
|
||||
};
|
||||
|
||||
public owner: Owner = {
|
||||
address: '',
|
||||
name: '',
|
||||
currency: 'usd',
|
||||
tax: false,
|
||||
taxValue: 0,
|
||||
vat: false,
|
||||
vatValue: 0,
|
||||
first: '',
|
||||
last: '',
|
||||
email: '',
|
||||
street: '',
|
||||
city: '',
|
||||
state: '',
|
||||
postal: '',
|
||||
phone: '',
|
||||
paid: false,
|
||||
website: '',
|
||||
country: '',
|
||||
zats: false,
|
||||
invoices: false,
|
||||
expiration: new Date(Date.now()).toISOString(),
|
||||
payconf: false,
|
||||
viewkey: ''
|
||||
};
|
||||
private invData_raw : string = '';
|
||||
private invData_buff : any = null;
|
||||
|
||||
public reportType = 0;
|
||||
public Status = 0;
|
||||
|
||||
constructor(private activatedRoute : ActivatedRoute,
|
||||
private http : HttpClient ) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.activatedRoute.queryParams.subscribe((params) => {
|
||||
var auth = 'Basic ' + Buffer.from(ConfigData.UsrPwd).toString('base64');
|
||||
this.reqHeaders = new HttpHeaders().set('Authorization', auth);
|
||||
this.activatedRoute.queryParams.subscribe((params) => {
|
||||
this.pmtData.ownerId = params["ownerid"];
|
||||
this.pmtData.invoice = params["invoice"];
|
||||
this.pmtData.invoice = params["invoiceNo"];
|
||||
this.pmtData.amount = params["amount"];
|
||||
this.pmtData.currency = params["currency"];
|
||||
this.pmtData.shortcode = params["shortcode"];
|
||||
|
||||
this.pmtData.shortcode = params["shortCode"];
|
||||
console.log(this.pmtData);
|
||||
|
||||
this.getInvoiceData( this.pmtData );
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
getInvoiceData( reqData : PmtData ) {
|
||||
|
||||
//
|
||||
// Verify owner id ( Status = 1 if not exists )
|
||||
// ( Status = 2 if service not available for user )
|
||||
//
|
||||
console.log('getOwner -> ', reqData.ownerId);
|
||||
const ownParams = new HttpParams().append('id', reqData.ownerId);
|
||||
let obs = this.http.get<{message:string, owner: any}>
|
||||
( this.beUrl+'api/ownerid',
|
||||
{ headers: this.reqHeaders,
|
||||
params: ownParams,
|
||||
observe: 'response'});
|
||||
obs.subscribe((OwnerDataResponse) => {
|
||||
//console.log('api/getowner', OwnerDataResponse.status);
|
||||
if (OwnerDataResponse.status == 200) {
|
||||
this.owner = OwnerDataResponse.body!.owner;
|
||||
console.log('Owner => ' + this.owner.name );
|
||||
if ( this.owner.payconf == false ) {
|
||||
// process data
|
||||
console.log("Owner check passed!!!");
|
||||
this.getXeroInvoiceData( reqData );
|
||||
} else {
|
||||
console.log("Owner check failed!!!")
|
||||
this.reportType = 2;
|
||||
};
|
||||
} else {
|
||||
if ( OwnerDataResponse.status == 204 ) {
|
||||
console.log('Res.Status = ' + OwnerDataResponse.status)
|
||||
console.log('Owner id not found!!!');
|
||||
this.reportType = 1;
|
||||
}
|
||||
}});
|
||||
|
||||
}
|
||||
|
||||
getXeroInvoiceData( reqData : PmtData ) {
|
||||
|
||||
// Call Xero API
|
||||
let url : string = "http://localhost:3000/xero/" + reqData.invoice;
|
||||
this.http
|
||||
.get<any>(url)
|
||||
.subscribe( data => {
|
||||
console.log('Data >>> ' + data);
|
||||
this.invData_raw = data.message.replaceAll("'",'"');
|
||||
this.invData_buff = JSON.parse(this.invData_raw);
|
||||
console.log('Invoice : >> ' + this.invData_raw);
|
||||
this.invData.inv_Type = this.invData_buff.type;
|
||||
this.invData.inv_Id = this.invData_buff.invoiceID;
|
||||
this.invData.inv_No = this.invData_buff.invoiceNumber;
|
||||
this.invData.inv_Contact = this.invData_buff.contact.name;
|
||||
this.invData.inv_Currency = this.invData_buff.currencyCode;
|
||||
this.invData.inv_CurrencyRate = this.invData_buff.currencyRate;
|
||||
this.invData.inv_Total = this.invData_buff.total;
|
||||
this.invData.inv_Status = this.invData_buff.status;
|
||||
this.invData.inv_Date = new Date(this.invData_buff.date);
|
||||
this.invData.inv_shortCode = reqData.shortcode;
|
||||
// invoice number found, test if it's type is correct
|
||||
if ( this.invData.inv_Type == 'ACCREC' ) {
|
||||
console.log('Invoice type is correct!!');
|
||||
// Test if invoice is not already paid
|
||||
if ( this.invData.inv_Status == 'AUTHORISED') {
|
||||
console.log('invoice is payable');
|
||||
// Test if Invoice's currency is supported
|
||||
if ( this.invData.inv_Currency == reqData.currency ) {
|
||||
console.log('Invoice currency supported');
|
||||
// Test if requested amount is as reported by Xero
|
||||
if ( this.invData.inv_Total == reqData.amount ) {
|
||||
console.log('Invoice amount Ok - create Order');
|
||||
//
|
||||
// =====> Create order here
|
||||
//
|
||||
} else {
|
||||
console.log('Invoice amount does not match')
|
||||
this.reportType = 8;
|
||||
}
|
||||
} else {
|
||||
console.log('Invoice currency not supported');
|
||||
this.reportType = 7;
|
||||
}
|
||||
} else {
|
||||
console.log('Invoice already paid');
|
||||
this.reportType = 6;
|
||||
}
|
||||
} else {
|
||||
console.log('Invoice type is invalid' );
|
||||
this.reportType = 5;
|
||||
}
|
||||
// Save invData in database
|
||||
},
|
||||
error => {
|
||||
if ( error.status == 404 ) {
|
||||
this.reportType = 4;
|
||||
console.log('Invoice not found (' + JSON.stringify(error) +')' );
|
||||
} else {
|
||||
this.reportType = 3;
|
||||
console.log('Xero server inaccesible! (' + JSON.stringify(error) + ')');
|
||||
}
|
||||
return 2;
|
||||
},
|
||||
() => {
|
||||
console.log("Request complete")
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
http://localhost:4200/pmtservice?ownerid=Rene&amount=30¤cy=USD&invoice=INV-003234&shortcode=abcde
|
||||
http://localhost:4200/pmtservice?ownerid=62cca13f5530331e2a97c78e&invoiceNo=INV-0034¤cy=USD&amount=753.95&shortCode=!w8T62
|
13
src/app/pmtservice/xeroinvoice.model.ts
Normal file
13
src/app/pmtservice/xeroinvoice.model.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
export interface XeroInvoice {
|
||||
inv_Type : String;
|
||||
inv_Id : String;
|
||||
inv_No : String;
|
||||
inv_Contact : String;
|
||||
inv_Currency : String;
|
||||
inv_CurrencyRate : number;
|
||||
inv_Total : number;
|
||||
inv_Status : String;
|
||||
inv_Date : Date;
|
||||
inv_shortCode : String;
|
||||
inv_ProcDate : Date;
|
||||
}
|
|
@ -23,3 +23,8 @@
|
|||
.mat-slide-toggle-content {
|
||||
font-family: 'Spartan', sans-serif;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,50 +1,58 @@
|
|||
|
||||
<div class="settings-title">Settings</div>
|
||||
|
||||
<div class="container" style="margin-top: 10px;">
|
||||
<div class="container"
|
||||
style="margin-top: 10px;
|
||||
height: 430px;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;">
|
||||
<mat-tab-group mat-tab-align-tabs="start">
|
||||
<mat-tab label="Main">
|
||||
<mat-dialog-content [formGroup]="settingsForm">
|
||||
<mat-form-field class="settings-field" [style.width.%]="100">
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput
|
||||
width="100%"
|
||||
placeholder="Name"
|
||||
formControlName="name">
|
||||
</mat-form-field>
|
||||
<mat-form-field [style.width.%]="100" >
|
||||
<mat-label>Currency</mat-label>
|
||||
<mat-select formControlName="currency">
|
||||
<mat-option *ngFor="let coin of coins"
|
||||
[value]="coin.symbol">
|
||||
{{coin.label}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-slide-toggle formControlName="useZats"
|
||||
class="settings-toggle"
|
||||
(change)="onChange($event)">
|
||||
Use zatoshis?
|
||||
</mat-slide-toggle>
|
||||
<pre></pre>
|
||||
<mat-slide-toggle formControlName="useVKey"
|
||||
class="settings-toggle"
|
||||
(change)="onChangeVKeyOn($event)">
|
||||
Confirm payments?
|
||||
</mat-slide-toggle>
|
||||
<pre></pre>
|
||||
<mat-form-field [style.width.%]="100">
|
||||
<mat-label>Viewing key</mat-label>
|
||||
<input matInput placeholder="Your wallet viewing key"
|
||||
formControlName="vKey">
|
||||
</mat-form-field>
|
||||
<mat-tab label="Main" style="height: 400px;">
|
||||
<div class="container" style="margin-bottom: 20px;">
|
||||
<mat-dialog-content [formGroup]="settingsForm">
|
||||
<mat-form-field class="settings-field" [style.width.%]="100">
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput
|
||||
width="100%"
|
||||
placeholder="Name"
|
||||
formControlName="name">
|
||||
</mat-form-field>
|
||||
<mat-form-field [style.width.%]="100" >
|
||||
<mat-label>Currency</mat-label>
|
||||
<mat-select formControlName="currency">
|
||||
<mat-option *ngFor="let coin of coins"
|
||||
[value]="coin.symbol">
|
||||
{{coin.label}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-slide-toggle formControlName="useZats"
|
||||
class="settings-toggle"
|
||||
(change)="onChange($event)">
|
||||
Use zatoshis?
|
||||
</mat-slide-toggle>
|
||||
<pre></pre>
|
||||
<mat-slide-toggle formControlName="useVKey"
|
||||
class="settings-toggle"
|
||||
(change)="onChangeVKeyOn($event)">
|
||||
Confirm payments?
|
||||
</mat-slide-toggle>
|
||||
<pre></pre>
|
||||
<mat-form-field class="full-width"
|
||||
appearance="fill">
|
||||
<mat-label>Viewing key</mat-label>
|
||||
<textarea matInput placeholder="Your wallet viewing key"
|
||||
formControlName="vKey">
|
||||
</textarea>
|
||||
</mat-form-field>
|
||||
|
||||
</mat-dialog-content>
|
||||
</mat-dialog-content>
|
||||
</div>
|
||||
|
||||
<mat-dialog-actions style="display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 12px;">
|
||||
<div class="container"
|
||||
style="display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;">
|
||||
<button mat-raised-button
|
||||
(click)="close()">
|
||||
Cancel
|
||||
|
@ -54,13 +62,25 @@
|
|||
(click)="save()">
|
||||
Save
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
</div>
|
||||
<div style="height: 20px;
|
||||
margin-top: 10px;">
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab label="Advanced">
|
||||
<div align="center">
|
||||
<a mat-raised-button color="primary" href="{{this.xeroLink}}">
|
||||
Link to Xero
|
||||
</a>
|
||||
<mat-tab label="Advanced"
|
||||
style="align-items: center;">
|
||||
<div style="height: 20px;
|
||||
margin-top: 10px;">
|
||||
</div>
|
||||
<div class="container"
|
||||
style="height: 300;">
|
||||
<p style="text-align:center">
|
||||
<a mat-raised-button
|
||||
color="primary"
|
||||
href="{{this.xeroLink}}">
|
||||
Link to Xero
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
|
|
|
@ -69,6 +69,7 @@ export class SettingsComponent implements OnInit {
|
|||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.settingsForm.get('vKey')!.disable();
|
||||
}
|
||||
|
||||
safeURL(s: string){
|
||||
|
|
|
@ -72,6 +72,9 @@ export class ViewerComponent implements OnInit {
|
|||
this.ownerUpdate.subscribe((owner) => {
|
||||
this.owner = owner;
|
||||
});
|
||||
|
||||
// console.log(this.owner._id);
|
||||
|
||||
this.userUpdate = userService.userUpdate;
|
||||
this.userUpdate.subscribe((user) => {
|
||||
this.user = user;
|
||||
|
|
BIN
src/assets/logo-new-white-orange_00.png
Normal file
BIN
src/assets/logo-new-white-orange_00.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 78 KiB |
BIN
src/assets/logo-new-white_01.png
Normal file
BIN
src/assets/logo-new-white_01.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
Loading…
Reference in a new issue