Implement Data Export

Squashed commit of the following:

commit 0c63521f38df66cd8a3d20e67d7b653f42f6b1bc
Author: Rene Vergara <rene@vergara.network>
Date:   Tue Oct 11 14:18:20 2022 -0500

    Update version

commit f80232d911
Author: Rene Vergara <rene@vergara.network>
Date:   Tue Oct 11 11:52:56 2022 -0500

    Adjust look and feel for data export

commit 53c3d5e78e
Author: Rene Vergara A <rvergara59@protonmail.com>
Date:   Sun Oct 9 09:40:58 2022 -0500

    Db-export component ready for testing

commit d4cdcd2a9a
Merge: da4413a 41b899b
Author: Rene Vergara A <rvergara59@protonmail.com>
Date:   Sat Oct 8 09:08:33 2022 -0500

    Merge branch 'master' of https://gitlab.com/pitmutt/zgo into dbexport

commit da4413af85
Author: Rene Vergara A <rvergara59@protonmail.com>
Date:   Sat Oct 8 09:06:56 2022 -0500

    Data Export component in progress...

commit 98d9360aad
Author: Rene Vergara A <rvergara59@protonmail.com>
Date:   Fri Oct 7 18:05:36 2022 -0500

    Angular updated to v14 - core

commit e78d4efa46
Author: Rene Vergara A <rvergara59@protonmail.com>
Date:   Fri Oct 7 17:59:17 2022 -0500

    Angular updated to v14 - cli

commit 9f2afbbff9
Author: Rene Vergara A <rvergara59@protonmail.com>
Date:   Fri Oct 7 17:52:44 2022 -0500

    Angular updated to v14

commit 21b1866a09
Merge: 3936371 72f4d67
Author: Rene Vergara A <rvergara59@protonmail.com>
Date:   Fri Oct 7 17:31:41 2022 -0500

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

commit 3936371c97
Author: Rene Vergara A <rvergara59@protonmail.com>
Date:   Fri Oct 7 17:31:24 2022 -0500

    Initial programming of Data Export

commit 7c9b4ef43a
Author: Rene Vergara A <rvergara59@protonmail.com>
Date:   Thu Sep 22 16:58:59 2022 -0500

    Few updates
This commit is contained in:
Rene Vergara 2022-10-11 14:20:06 -05:00
parent 41b899b361
commit cc881b38a1
Signed by: pitmutt
GPG key ID: 65122AD495A7F5B2
16 changed files with 3090 additions and 2911 deletions

Binary file not shown.

Binary file not shown.

View file

@ -5,9 +5,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## [Unreleased] ## [Unreleased]
## [1.3.2] - 2022-10-11
### Added
- New component added to export orders in CSV format. Allows users to download orders.
## [1.3.1] - 2022-10-08 ## [1.3.1] - 2022-10-08
## Fixed ### Fixed
- Bug [#7](https://gitlab.com/pitmutt/zgo/-/issues/7) for saving a viewing key. - Bug [#7](https://gitlab.com/pitmutt/zgo/-/issues/7) for saving a viewing key.

Binary file not shown.

5564
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "zgo", "name": "zgo",
"version": "1.3.1", "version": "1.3.2",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
@ -10,16 +10,17 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^14.0.5", "@angular-material-components/datetime-picker": "^8.0.0",
"@angular/cdk": "^13.3.9", "@angular/animations": "^14.2.5",
"@angular/common": "^14.0.5", "@angular/cdk": "^14.2.4",
"@angular/compiler": "^14.0.5", "@angular/common": "^14.2.5",
"@angular/core": "^14.0.5", "@angular/compiler": "^14.2.5",
"@angular/forms": "^14.0.5", "@angular/core": "^14.2.5",
"@angular/material": "^13.3.9", "@angular/forms": "^14.2.5",
"@angular/platform-browser": "^14.0.5", "@angular/material": "^14.2.4",
"@angular/platform-browser-dynamic": "^14.0.5", "@angular/platform-browser": "^14.2.5",
"@angular/router": "^14.0.5", "@angular/platform-browser-dynamic": "^14.2.5",
"@angular/router": "^14.2.5",
"@fortawesome/angular-fontawesome": "^0.10.2", "@fortawesome/angular-fontawesome": "^0.10.2",
"@fortawesome/fontawesome-free": "^6.1.1", "@fortawesome/fontawesome-free": "^6.1.1",
"@fortawesome/fontawesome-svg-core": "^6.1.0", "@fortawesome/fontawesome-svg-core": "^6.1.0",
@ -28,6 +29,7 @@
"@fortawesome/free-solid-svg-icons": "^6.1.0", "@fortawesome/free-solid-svg-icons": "^6.1.0",
"@supercharge/request-ip": "^1.1.2", "@supercharge/request-ip": "^1.1.2",
"angular-local-storage": "^0.7.1", "angular-local-storage": "^0.7.1",
"angular-material-datepicker": "^1.0.2",
"async": "^3.2.2", "async": "^3.2.2",
"coingecko-api": "^1.0.10", "coingecko-api": "^1.0.10",
"easyqrcodejs": "^4.4.6", "easyqrcodejs": "^4.4.6",
@ -43,9 +45,9 @@
"zone.js": "~0.11.4" "zone.js": "~0.11.4"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^14.0.5", "@angular-devkit/build-angular": "^14.2.5",
"@angular/cli": "^14.0.6", "@angular/cli": "^14.2.5",
"@angular/compiler-cli": "^14.0.5", "@angular/compiler-cli": "^14.2.5",
"@types/jasmine": "~3.8.0", "@types/jasmine": "~3.8.0",
"@types/node": "^12.20.33", "@types/node": "^12.20.33",
"@types/request": "^2.48.8", "@types/request": "^2.48.8",

View file

@ -1,41 +0,0 @@
{
"Invoice": { "InvoiceID": "96df0dff-43ec-4899-a7d9-e9d63ef12b19" },
"Account": { "Code": "001" },
"Date": "2022-08-25",
"Amount": 32.06
}
{
"Invoice": { "InvoiceNumber": "INV-0041" },
"Account": { "Code": "001" },
"Date": "2022-08-25",
"Amount": 32.06
}
POST -> https://api.xero.com/api.xro/2.0/Payments
Authorization Bearer ey****
Xero-Tenant-Id 5ae23193-e4b4-4001-99e9-b98634d6040e
Accept application/json
Content-Type application/json
Payload
{
"Payments": [
{
"Invoice": {
"LineItems": [],
"InvoiceID": "00000000-0000-0000-0000-000000000000"
},
"Account": {
"Code": "970"
},
"Date": "2019-03-12",
"Amount": 1
}
]
}

View file

@ -18,6 +18,8 @@ import { MatStepperModule } from '@angular/material/stepper';
import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatTabsModule } from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatNativeDateModule } from '@angular/material/core';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
@ -48,6 +50,7 @@ import { PromptReceiptComponent } from './prompt-receipt/prompt-receipt.componen
import { NotifierComponent } from './notifier/notifier.component'; import { NotifierComponent } from './notifier/notifier.component';
import { PmtserviceComponent } from './pmtservice/pmtservice.component'; import { PmtserviceComponent } from './pmtservice/pmtservice.component';
import { XeroRegComponent } from './xeroreg/xeroreg.component'; import { XeroRegComponent } from './xeroreg/xeroreg.component';
import { DbExportComponent } from './db-export/db-export.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -76,7 +79,8 @@ import { XeroRegComponent } from './xeroreg/xeroreg.component';
PromptReceiptComponent, PromptReceiptComponent,
NotifierComponent, NotifierComponent,
PmtserviceComponent, PmtserviceComponent,
XeroRegComponent XeroRegComponent,
DbExportComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
@ -100,6 +104,8 @@ import { XeroRegComponent } from './xeroreg/xeroreg.component';
MatSlideToggleModule, MatSlideToggleModule,
MatSnackBarModule, MatSnackBarModule,
MatTabsModule, MatTabsModule,
MatDatepickerModule,
MatNativeDateModule,
BrowserAnimationsModule, BrowserAnimationsModule,
FontAwesomeModule FontAwesomeModule
], ],

View file

@ -0,0 +1,68 @@
* {
font-family: 'Spartan', sans-serif;
font-size: 11px;
}
.description {
padding-top: 30px;
padding-bottom: 20px;
font-size: 14px;
font-weight: 700;
text-align: center;
}
.datepicker {
border-color: dimgray;
border-width: 3px;
border-radius: 8px;
background-color: white;
}
.noorders {
font-size: 14px;
font-weight: 700;
text-align: center;
padding-top: 20px;
padding-bottom: 20px;
border-color: dimgray;
border-width: 3px;
border-radius: 8px;
background-color: #f9e79f;
}
.settings-title {
font-family: 'Spartan', sans-serif;
background: #ff5722;
color: white;
font-size: 30px;
text-align: center;
padding: 5px;
}
.daterange {
font-size: 13px;
font-weight: 700;
padding-bottom: 15px;
}
.downloadbtn {
min-width: 120px;
max-width: 150px;
height: 25px;
border-color: dimgray;
border-width: 3px;
border-radius: 8px;
box-shadow: lightgray;
font-family: 'Spartan', sans-serif;
background: #ff5722;
color: white;
font-size: 16px;
text-align: center;
padding: 6px;
}
::ng-deep .downloadbtntxt {
color: white;
font-size: 14px;
font-weight: 600;
}

View file

@ -0,0 +1,54 @@
<div class="settings-title">Export Orders</div>
<div class='description'>
Export orders in a .CSV format file
</div>
<div class="datepicker"
*ngIf="ordersOk()">
<mat-form-field appearance="fill">
<!--
<mat-label >Enter a date range</mat-label>
-->
<div class="daterange">Date range:</div>
<mat-date-range-input [formGroup]="range" [rangePicker]="picker">
<input matStartDate formControlName="start" placeholder="Start date">
<input matEndDate formControlName="end" placeholder="End date">
</mat-date-range-input>
<mat-hint>MM/DD/YYYY MM/DD/YYYY</mat-hint>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-date-range-picker #picker></mat-date-range-picker>
<mat-error *ngIf="range.controls.start.hasError('matStartDateInvalid')">Invalid start date</mat-error>
<mat-error *ngIf="range.controls.end.hasError('matEndDateInvalid')">Invalid end date</mat-error>
</mat-form-field>
<br>
<br>
<br>
</div>
<div class="noorders"
*ngIf="!ordersOk()">
<br>
You have no orders created.
<br>
Nothing to do.
<br>
<br>
<br>
</div>
<br>
<br>
<div style="display: flex;
justify-content: space-between;
align-items: center;">
<button mat-raised-button
(click)="closedbExport()">
Close
</button>
<a mat-raised-button *ngIf="checkReady()" color="primary" [href]="fileUrl"
download="orders.csv">Download</a>
</div>
<div style="height: 20px;
margin-top: 10px;">
</div>

View file

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DbExportComponent } from './db-export.component';
describe('DbExportComponent', () => {
let component: DbExportComponent;
let fixture: ComponentFixture<DbExportComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DbExportComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(DbExportComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,146 @@
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import { DomSanitizer } from '@angular/platform-browser';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { Observable } from 'rxjs';
import { Order } from '../order/order.model';
import { FullnodeService } from '../fullnode.service';
import { UserService } from '../user.service';
import { Owner } from '../owner.model';
import { OrderService } from '../order/order.service';
import { NotifierService } from '../notifier.service';
@Component({
selector: 'app-db-export',
templateUrl: './db-export.component.html',
styleUrls: ['./db-export.component.css']
})
export class DbExportComponent implements OnInit {
public orders: Order[] = [];
public ownerUpdate: Observable<Owner>;
public ordersUpdate: Observable<Order[]>;
fileUrl : any;
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: '',
crmToken: ''
};
_ordersOk = false;
range = new FormGroup({
start: new FormControl<Date | null>(null),
end: new FormControl<Date | null>(null),
});
constructor(private notifierService : NotifierService,
private dialogRef: MatDialogRef<DbExportComponent>,
private sanitizer: DomSanitizer,
public orderService: OrderService,
public userService: UserService) {
this.ownerUpdate = userService.ownerUpdate;
this.orderService.getAllOrders();
this.ordersUpdate = orderService.allOrdersUpdate;
}
ngOnInit(): void {
console.log('db-export Init -->');
this.owner = this.userService.currentOwner();
console.log(this.owner.name);
console.log(this.range);
this.ordersUpdate.subscribe((orders) => {
this.orders = orders;
// console.log('Order -> ' + this.orders[0].timestamp);
if( this.orders.length != 0 ) {
this._ordersOk = true
}
});
}
ordersOk() : boolean {
return this._ordersOk;
}
checkReady() : boolean {
var data : string = '';
var chkRdy : boolean = false;
if ( (this.range.value.start != null ) &&
(this.range.value.end != null) ) {
// process order list
const formatter = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 8,
maximumFractionDigits: 8,
});
// create header
data = '"Date","Order ID","Currency","Closed?","Amount","Rate","ZEC","Paid?","Invoice"' + "\n";
var iniDate = new Date(this.range.value.start);
var endDate = new Date(this.range.value.end);
for (let i=0; i < this.orders.length; i++){
var date = new Date(this.orders[i]!.timestamp!);
var orderid = String(this.orders[i]._id);
var closed = this.orders[i].closed ? 'Yes' : 'No';
/*
console.log('Order No. ' +
this.orders[i]._id! + ' - totalZec = ' +
this.orders[i].totalZec);
*/
var paid = this.orders[i].paid ? 'Yes' : 'No';
if ( (date >= iniDate) && (date <= endDate) ) {
data = data +
date.getFullYear() + '-' +
(date.getMonth()+1).toString().padStart(2,'0') + '-' +
date.getDate().toString().padStart(2,'0')
+ ',' +
orderid + ',' +
this.orders[i].currency + ',' +
closed + ',' +
this.orders[i].total + ',' +
this.orders[i].price! + ',' +
this.orders[i].totalZec + ',' +
paid + ',"' +
this.orders[i].externalInvoice + '"' +
'\n';
}
}
const blob = new Blob([data], { type: 'application/octet-stream' });
this.fileUrl = this.sanitizer.bypassSecurityTrustResourceUrl(window.URL.createObjectURL(blob));
chkRdy = true;
}
return chkRdy;
}
closedbExport() {
this.dialogRef.close();
}
}

View file

@ -54,7 +54,9 @@ export class FullnodeService{
constructor(private http: HttpClient, public userService: UserService){ constructor(private http: HttpClient, public userService: UserService){
var auth = 'Basic ' + Buffer.from(ConfigData.UsrPwd).toString('base64'); var auth = 'Basic ' + Buffer.from(ConfigData.UsrPwd).toString('base64');
console.log('auth: ' + auth);
this.reqHeaders = new HttpHeaders().set('Authorization', auth); this.reqHeaders = new HttpHeaders().set('Authorization', auth);
console.log(this.reqHeaders);
this.ownerUpdate = userService.ownerUpdate; this.ownerUpdate = userService.ownerUpdate;
this.getAddr(); this.getAddr();
this.getHeight(); this.getHeight();

View file

@ -97,8 +97,8 @@ img.icon{
} }
.orderListTitle { .orderListTitle {
font-family: 'Roboto Mono'; font-family: 'Roboto Mono' !important;
font-size: 15px; font-size: 14px;
font-weight: 600; font-weight: 600;
} }

View file

@ -1,9 +1,23 @@
<app-header></app-header> <app-header></app-header>
<div align="center"> <div align="center">
<h3 class="text">{{(ownerUpdate | async)!.name}}</h3> <h3 class="text">{{(ownerUpdate | async)!.name}}</h3>
<table >
<tr>
<td width="45%">
<button class="text" mat-raised-button [routerLink]="['/shop']" color="primary"> <button class="text" mat-raised-button [routerLink]="['/shop']" color="primary">
Back to Shop Back to Shop
</button> </button>
</td>
<td width="10%">
</td>
<td width="45%">
<button mat-raised-button color="primary"
class="text" (click)="openDbExport()">
Export Orders
</button>
</td>
</tr>
</table>
</div> </div>
<table class="totalsTbl" width="100%"> <table class="totalsTbl" width="100%">
<tr class="totalsHdr"> <tr class="totalsHdr">
@ -31,12 +45,13 @@
<div class="orderList"> <div class="orderList">
<mat-accordion *ngIf = "orders.length > 0"> <mat-accordion *ngIf = "orders.length > 0">
<mat-expansion-panel *ngFor = "let order of orders"> <mat-expansion-panel *ngFor = "let order of orders">
<mat-expansion-panel-header [collapsedHeight]="'30px'" [expandedHeight]="'30px'" > <mat-expansion-panel-header [collapsedHeight]="'35px'" [expandedHeight]="'30px'" >
<mat-panel-title> <mat-panel-title>
<div class="orderListTitle"> <div class="orderListTitle">
<img src="/assets/zec_rv.png" <img src="/assets/zec_rv.png"
style="height: 16px; style="height: 14px;
margin-bottom: -2px;" margin-bottom: -2px;
padding-right: 3px;"
>{{order.totalZec | number: '1.08'}} >{{order.totalZec | number: '1.08'}}
</div> </div>
</mat-panel-title> </mat-panel-title>
@ -50,7 +65,7 @@
<td align="center" <td align="center"
style="font-family: 'Roboto Mono' !important; style="font-family: 'Roboto Mono' !important;
font-weight: 700 ; font-weight: 700 ;
font-size: 15px;"> font-size: 14px;">
{{order.timestamp | date: 'YYYY-MM-dd, HH:mm'}} {{order.timestamp | date: 'YYYY-MM-dd, HH:mm'}}
</td> </td>
</tr> </tr>

View file

@ -9,6 +9,7 @@ import { OrderService } from '../order/order.service';
import { MatDialog, MatDialogConfig} from '@angular/material/dialog'; import { MatDialog, MatDialogConfig} from '@angular/material/dialog';
import { PromptInvoiceComponent } from '../prompt-invoice/prompt-invoice.component'; import { PromptInvoiceComponent } from '../prompt-invoice/prompt-invoice.component';
import { PromptReceiptComponent } from '../prompt-receipt/prompt-receipt.component'; import { PromptReceiptComponent } from '../prompt-receipt/prompt-receipt.component';
import { DbExportComponent } from '../db-export/db-export.component';
import { faTimes } from '@fortawesome/free-solid-svg-icons'; import { faTimes } from '@fortawesome/free-solid-svg-icons';
import { faTimesCircle } from '@fortawesome/free-solid-svg-icons'; import { faTimesCircle } from '@fortawesome/free-solid-svg-icons';
@ -70,7 +71,6 @@ export class ListOrdersComponent implements OnInit, OnDestroy{
// ------------------------------------- // -------------------------------------
constructor( constructor(
public orderService: OrderService, public orderService: OrderService,
public userService: UserService, public userService: UserService,
@ -169,4 +169,20 @@ export class ListOrdersComponent implements OnInit, OnDestroy{
}); });
} }
openDbExport(){
const dialogConfig = new MatDialogConfig();
console.log('openDbExport ---');
dialogConfig.disableClose = false;
dialogConfig.autoFocus = true;
dialogConfig.data = this.owner;
const dialogRef = this.dialog.open(DbExportComponent, dialogConfig);
dialogRef.afterClosed().subscribe((val) => {
console.log('Returning to order list');
});
}
} }