Merge branch 'xero' of gitlab.com:pitmutt/zgo into xero

This commit is contained in:
Rene Vergara 2022-08-19 07:57:24 -05:00
commit 5f05de246d
Signed by: pitmutt
GPG key ID: 65122AD495A7F5B2
17 changed files with 454 additions and 58 deletions

BIN
AccountingIntegration.odt Normal file

Binary file not shown.

BIN
AccountingIntegration.pdf Normal file

Binary file not shown.

View file

@ -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

Binary file not shown.

View file

@ -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">

View file

@ -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>

View file

@ -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;
}

View file

@ -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>

View file

@ -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")
});
}
}

View file

@ -1 +1 @@
http://localhost:4200/pmtservice?ownerid=Rene&amount=30&currency=USD&invoice=INV-003234&shortcode=abcde
http://localhost:4200/pmtservice?ownerid=62cca13f5530331e2a97c78e&invoiceNo=INV-0034&currency=USD&amount=753.95&shortCode=!w8T62

View 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;
}

View file

@ -23,3 +23,8 @@
.mat-slide-toggle-content {
font-family: 'Spartan', sans-serif;
}
.full-width {
width: 100%;
}

View file

@ -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,14 +62,26 @@
(click)="save()">
Save
</button>
</mat-dialog-actions>
</mat-tab>
<mat-tab label="Advanced">
<div align="center">
<a mat-raised-button color="primary" href="{{this.xeroLink}}">
Link to Xero
</a>
</div>
<div style="height: 20px;
margin-top: 10px;">
</div>
</mat-tab>
<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>
</div>

View file

@ -69,6 +69,7 @@ export class SettingsComponent implements OnInit {
}
ngOnInit() {
this.settingsForm.get('vKey')!.disable();
}
safeURL(s: string){

View file

@ -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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB