371 lines
17 KiB
JavaScript
371 lines
17 KiB
JavaScript
import React, { useRef, useEffect } from 'react';
|
|
import { useLocation } from 'react-router-dom';
|
|
import imageLogo from '../content/images/Logo.png';
|
|
import * as shared from '../scripts/shared.js';
|
|
import { jsPDF } from "jspdf";
|
|
|
|
const PageInvoiceOrEstimate = () => {
|
|
const canvasRefs = useRef([]);
|
|
const location = useLocation();
|
|
console.log("location:", location);
|
|
const props = location.state.data;
|
|
|
|
let styles = {
|
|
container: {
|
|
flexDirection: 'column',
|
|
padding: 20,
|
|
},
|
|
header: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
marginBottom: 20,
|
|
},
|
|
logo: {
|
|
width: 80,
|
|
height: 80,
|
|
marginRight: 20,
|
|
},
|
|
line: {
|
|
height: 1,
|
|
backgroundColor: '#000',
|
|
marginVertical: 10,
|
|
},
|
|
page: {
|
|
flexDirection: 'row',
|
|
backgroundColor: '#E4E4E4',
|
|
width: '100%',
|
|
height: '100%',
|
|
padding: 20,
|
|
},
|
|
section: {
|
|
margin: 10,
|
|
padding: 10,
|
|
flexGrow: 1,
|
|
},
|
|
};
|
|
console.log("PageInvoiceOrEstimate");
|
|
console.log(props);
|
|
|
|
|
|
const heightA4 = 1123;
|
|
const widthA4 = 794;
|
|
const borderPage = 75;
|
|
const marginTable = 50;
|
|
const spacingLabel = marginTable - 20;
|
|
const heightLine = 25;
|
|
// positioning
|
|
const heightMyBusiness = 180;
|
|
const heightIssue = 325;
|
|
const heightBankMyBusiness = 420;
|
|
const heightBillToSite = 560;
|
|
const heightHeadTable = 800;
|
|
const heightRowMax = 1200; // update this !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
const maxRows = Math.floor((heightRowMax - heightHeadTable - heightLine) / (heightLine + spacingLabel));
|
|
console.log("maxRows:", maxRows);
|
|
const xPositionTableColumns = [120, 420, 520, 620];
|
|
const typeForm = props.typeForm == "1" ? "Invoice" : "Estimate";
|
|
console.log("typeForm:", typeForm);
|
|
let countRowsTemp = 0;
|
|
if (props["type" + typeForm] == "0") {
|
|
countRowsTemp = props["quantityGoods" + typeForm];
|
|
} else {
|
|
for (let indexService = 0; indexService < props["quantityServices" + typeForm]; indexService++) {
|
|
countRowsTemp += props["quantityBillingPeriodService" + typeForm + (indexService + 1)];
|
|
}
|
|
}
|
|
const countRows = countRowsTemp;
|
|
console.log("countRows:", countRows);
|
|
const countPages = Math.ceil(props["quantityServices" + typeForm] / maxRows);
|
|
console.log("countPages:", countPages);
|
|
const nameFont = "Arial";
|
|
|
|
const downloadPdf = (canvas, index) => {
|
|
const pdf = new jsPDF();
|
|
pdf.addImage(canvas.toDataURL("image/png"), "PNG", 0, 0);
|
|
pdf.save(`canvas${index}.pdf`);
|
|
};
|
|
|
|
|
|
const renderCanvases = () => {
|
|
const canvases = [];
|
|
for (let indexCanvas = 0; indexCanvas < countPages; indexCanvas++) {
|
|
canvases.push(
|
|
<div>
|
|
<canvas
|
|
key={indexCanvas}
|
|
ref={(ref) => (canvasRefs.current[indexCanvas] = ref)}
|
|
width={widthA4}
|
|
height={heightA4}
|
|
style={{ border: '1px solid black', margin: '10px' }}
|
|
/>
|
|
<button
|
|
onClick={() =>
|
|
downloadPdf(canvasRefs.current[indexCanvas], indexCanvas)
|
|
}
|
|
>
|
|
Download as PDF
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|
|
return canvases;
|
|
};
|
|
|
|
useEffect(() => {
|
|
let hasGoods = props["type" + typeForm] == "0";
|
|
let hasServices = props["type" + typeForm] == "1";
|
|
let countGoods = props["quantityGoods" + typeForm];
|
|
let countServices = props["quantityServices" + typeForm];
|
|
let indexGoodOrService = -1;
|
|
let indexSubservice = -1;
|
|
let indexRow = -1;
|
|
let pages = [];
|
|
let page;
|
|
let isRequiredPageNew = true;
|
|
while ((hasGoods && indexGoodOrService < countGoods - 1) || (hasServices && indexGoodOrService < countServices && indexRow < countRows - 1)) {
|
|
if (isRequiredPageNew) {
|
|
page = {
|
|
hasServices: hasServices,
|
|
hasGoods: hasGoods,
|
|
services: [],
|
|
goods: [],
|
|
total: 0,
|
|
hasTotal: false,
|
|
};
|
|
isRequiredPageNew = false;
|
|
}
|
|
indexRow++;
|
|
if (hasGoods) {
|
|
indexGoodOrService++;
|
|
let good = {
|
|
description: props["descriptionGood" + typeForm + (indexGoodOrService + 1)],
|
|
quantity: props["quantityGood" + typeForm + (indexGoodOrService + 1)],
|
|
rate: props["rateGood" + typeForm + (indexGoodOrService + 1)],
|
|
};
|
|
good["subtotal"] = (good.quantity * good.rate).toFixed(2);
|
|
page.goods.push(good);
|
|
page.total += Number(good.subtotal);
|
|
}
|
|
if (hasServices) {
|
|
indexSubservice++;
|
|
if (indexGoodOrService == -1 || indexSubservice == props["quantityBillingPeriodService" + typeForm + (indexGoodOrService + 1)]) {
|
|
indexSubservice = -1;
|
|
indexGoodOrService++;
|
|
indexRow--;
|
|
} else {
|
|
let service = {
|
|
description: props["descriptionIncrementService" + typeForm + (indexGoodOrService + 1) + "s" + (indexSubservice + 1)],
|
|
quantity: props["quantityRatePeriodsIncrementService" + typeForm + (indexGoodOrService + 1) + "s" + (indexSubservice + 1)],
|
|
rate: props["rateService" + typeForm + (indexGoodOrService + 1)],
|
|
};
|
|
service["subtotal"] = (service.quantity * service.rate).toFixed(2);
|
|
page.services.push(service);
|
|
page.total += Number(service.subtotal);
|
|
}
|
|
}
|
|
if (indexRow >= maxRows) {
|
|
indexRow = 0;
|
|
pages.push(page);
|
|
isRequiredPageNew = true;
|
|
}
|
|
if (indexRow == countRows - 1) {
|
|
page.hasTotal = true;
|
|
pages.push(page);
|
|
break;
|
|
}
|
|
}
|
|
console.log("pages:", pages);
|
|
|
|
|
|
let indexPage = -1;
|
|
canvasRefs.current.forEach((canvasRef) => {
|
|
indexPage++;
|
|
let page = pages[indexPage];
|
|
console.log("plotting page:", indexPage, page);
|
|
const canvas = canvasRef;
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
// Clear the canvas
|
|
ctx.clearRect(0, 0, widthA4, heightA4);
|
|
ctx.fillStyle = '#fff';
|
|
ctx.fillRect(0, 0, widthA4, heightA4);
|
|
ctx.fillStyle = '#000';
|
|
|
|
plot(ctx, page);
|
|
});
|
|
}, []);
|
|
|
|
const plot = (context, page) => { // typeForm, do_goods, rows_g, n_goods, do_services, rows_s, n_services, n_subs, ratedisc, date_due, my_ref) {
|
|
plotPageLayout(context);
|
|
plotPageInterpersonal(context);
|
|
plotHeadingsTableInvoiceOrEstimate(context);
|
|
|
|
let currency = props.currency;
|
|
|
|
if (page.hasServices) {
|
|
context.font = '12px ' + nameFont;
|
|
// page.services.map((service, index) => {
|
|
let service;
|
|
for (let indexService = 0; indexService < page.services.length; indexService++) {
|
|
service = page.services[indexService];
|
|
context.fillText(service.description, xPositionTableColumns[0], heightHeadTable + (indexService + 1) * heightLine);
|
|
context.fillText(service.quantity, xPositionTableColumns[1], heightHeadTable + (indexService + 1) * heightLine);
|
|
context.fillText(service.rate, xPositionTableColumns[2], heightHeadTable + (indexService + 1) * heightLine);
|
|
context.fillText(service.subtotal, xPositionTableColumns[3], heightHeadTable + (indexService + 1) * heightLine);
|
|
};
|
|
}
|
|
if (page.hasGoods) {
|
|
context.font = '12px ' + nameFont;
|
|
// page.goods.map((good, index) => {
|
|
let good;
|
|
for (let indexGood = 0; indexGood < page.goods.length; indexGood++) {
|
|
good = page.goods[indexGood];
|
|
context.fillText(good.description, xPositionTableColumns[0], heightHeadTable + (indexGood + 1) * heightLine);
|
|
context.fillText(good.quantity, xPositionTableColumns[1], heightHeadTable + (indexGood + 1) * heightLine);
|
|
context.fillText(good.rate, xPositionTableColumns[2], heightHeadTable + (indexGood + 1) * heightLine);
|
|
context.fillText(good.subtotal, xPositionTableColumns[3], heightHeadTable + (indexGood + 1) * heightLine);
|
|
};
|
|
}
|
|
if (page.hasTotal) {
|
|
context.font = '12px ' + nameFont;
|
|
let heightRowTotal = heightHeadTable + (1 + (page.hasGoods ? page.goods.length : page.services.length)) * heightLine;
|
|
console.log("page total: ", page.total);
|
|
context.fillText(currency + page.total.toFixed(2), xPositionTableColumns[3] - 10, heightRowTotal);
|
|
context.fillText('TOTAL:', xPositionTableColumns[3] - borderPage - 10, heightRowTotal);
|
|
}
|
|
}
|
|
|
|
const plotPageLayout = (context) => {
|
|
context.beginPath();
|
|
context.font = '20px ' + nameFont;
|
|
context.fillText(typeForm, borderPage, 100);
|
|
// context.fillText('ESTIMATE', borderPage, 100);
|
|
context.font = 'bold 12px ' + nameFont;
|
|
context.fillText('BANK NAME:', borderPage, heightBankMyBusiness);
|
|
context.fillText('ACCOUNT NAME:', borderPage, heightBankMyBusiness + heightLine);
|
|
context.fillText('ACCOUNT NUMBER:', borderPage, heightBankMyBusiness + heightLine * 2);
|
|
context.fillText('SORT CODE:', borderPage, heightBankMyBusiness + heightLine * 3);
|
|
context.font = 'bold 12px ' + nameFont;
|
|
context.fillText('ISSUE DATE:', widthA4 / 2 + marginTable, heightIssue);
|
|
context.fillText('DUE DATE:', widthA4 / 2 + marginTable, heightIssue + heightLine);
|
|
context.fillText('REFERENCE:', widthA4 / 2 + marginTable, heightIssue + heightLine * 2);
|
|
context.font = 'bold 12px ' + nameFont;
|
|
context.fillText('BILL TO:', borderPage, heightBillToSite);
|
|
context.fillText('SITE / LOCATION:', widthA4 / 2 + marginTable, heightBillToSite);
|
|
context.stroke();
|
|
}
|
|
|
|
const plotPageInterpersonal = (context) => {
|
|
context.font = '14px ' + nameFont;
|
|
context.fillText(props.nameMyBusiness, borderPage, heightMyBusiness );
|
|
context.font = '12px ' + nameFont;
|
|
context.fillText(props.address1MyBusiness, borderPage, heightMyBusiness + heightLine * 1);
|
|
context.fillText(props.address2MyBusiness, borderPage, heightMyBusiness + heightLine * 2);
|
|
context.fillText(props.address3MyBusiness, borderPage, heightMyBusiness + heightLine * 3);
|
|
context.fillText(props.address4MyBusiness, borderPage, heightMyBusiness + heightLine * 4);
|
|
context.fillText(props.address5MyBusiness, borderPage, heightMyBusiness + heightLine * 5);
|
|
context.fillText(props.emailMyBusiness, borderPage, heightMyBusiness + heightLine * 6);
|
|
context.fillText(props.companyNumberMyBusiness, borderPage, heightMyBusiness + heightLine * 7);
|
|
|
|
// logo
|
|
var img = new Image();
|
|
img.crossOrigin = "anonymous";
|
|
// CORS_ALLOW_ALL_ORIGINS = true;
|
|
img.style.border = 'none';
|
|
img.src = imageLogo; // "https://raw.githubusercontent.com/Teddy-1024/Neural_Network/master/Deane_logo.png";
|
|
let sz_img = 200;
|
|
img.onload = function() {
|
|
context.drawImage(img, widthA4 - borderPage * 3 / 2 -sz_img, borderPage, sz_img, sz_img); // , 0, 0, 0, 0, borderPage + 25, borderPage + 2, sz_img, sz_img);
|
|
context.stroke();
|
|
}
|
|
|
|
context.font = '12px ' + nameFont;
|
|
context.fillText(props.nameBankMyBusiness, borderPage + 150, heightBankMyBusiness);
|
|
context.fillText(props.accountNameBankMyBusiness, borderPage + 150, heightBankMyBusiness + heightLine);
|
|
context.fillText(props.accountNumberBankMyBusiness, borderPage + 150, heightBankMyBusiness + heightLine * 2);
|
|
context.fillText(props.sortCodeBankMyBusiness, borderPage + 150, heightBankMyBusiness + heightLine * 3);
|
|
|
|
context.font = '12px ' + nameFont;
|
|
context.fillText(getDateIssue(), widthA4 / 2 + marginTable + 120, heightIssue);
|
|
context.fillText(getDateDue(), widthA4 / 2 + marginTable + 120, heightIssue + heightLine);
|
|
context.fillText(getReference(), widthA4 / 2 + marginTable + 120, heightIssue + heightLine * 2);
|
|
|
|
context.font = '12px ' + nameFont;
|
|
context.fillText(props.nameTheirBusiness, borderPage, heightBillToSite + heightLine * 1);
|
|
context.fillText(props.address1TheirBusiness, borderPage, heightBillToSite + heightLine * 2);
|
|
context.fillText(props.address2TheirBusiness, borderPage, heightBillToSite + heightLine * 3);
|
|
context.fillText(props.address3TheirBusiness, borderPage, heightBillToSite + heightLine * 4);
|
|
context.fillText(props.address4TheirBusiness, borderPage, heightBillToSite + heightLine * 5);
|
|
context.fillText(props.address5TheirBusiness, borderPage, heightBillToSite + heightLine * 6);
|
|
context.fillText(props.emailTheirBusiness, borderPage, heightBillToSite + heightLine * 7);
|
|
context.fillText(props.phoneTheirBusiness, borderPage, heightBillToSite + heightLine * 8);
|
|
|
|
context.fillText(props.nameContactTheirBusiness, widthA4 / 2 + marginTable, heightBillToSite + heightLine * 1);
|
|
context.fillText(props.address1ContactTheirBusiness, widthA4 / 2 + marginTable, heightBillToSite + heightLine * 2);
|
|
context.fillText(props.address2ContactTheirBusiness, widthA4 / 2 + marginTable, heightBillToSite + heightLine * 3);
|
|
context.fillText(props.address3ContactTheirBusiness, widthA4 / 2 + marginTable, heightBillToSite + heightLine * 4);
|
|
context.fillText(props.address4ContactTheirBusiness, widthA4 / 2 + marginTable, heightBillToSite + heightLine * 5);
|
|
context.fillText(props.address5ContactTheirBusiness, widthA4 / 2 + marginTable, heightBillToSite + heightLine * 6);
|
|
context.stroke();
|
|
}
|
|
|
|
const getDateIssue = () => {
|
|
let date = new Date(props["dateBilling" + typeForm]);
|
|
return shared.date2str(new Date(date.getFullYear(), date.getMonth(), -1));
|
|
}
|
|
|
|
const getDateDue = () => {
|
|
let date_0 = new Date(props.dateBillingInvoice);
|
|
let y0 = date_0.getFullYear();
|
|
let m0 = date_0.getMonth();
|
|
let date_1 = new Date(y0, m0 + 1, 1);
|
|
let n_working_days = 1;
|
|
let n_days_total = 4;
|
|
let date_temp = date_1;
|
|
for (let i = 1; i < 6; i++) {
|
|
date_temp.setDate(date_temp.getDate() + 1);
|
|
let billday = date_temp.getDay();
|
|
if (!(billday == 0 || billday == 6)) {
|
|
n_working_days++;
|
|
}
|
|
if (n_working_days == n_days_total) {
|
|
break;
|
|
}
|
|
}
|
|
let date_2 = date_temp;
|
|
return convertDateToString(date_2);
|
|
}
|
|
|
|
const convertDateToString = (date) => {
|
|
return date.getFullYear().toString() + '-' + (date.getMonth() + 1).toString().padStart(2, '0') + '-' + date.getDate().toString().padStart(2, '0');
|
|
}
|
|
|
|
const getReference = (pageHasGoods) => {
|
|
let name;
|
|
if (pageHasGoods) {
|
|
name = "goods" + convertDateToString(new Date());
|
|
} else {
|
|
let week1 = props["idBillingPeriodFirstIncrementalService" + typeForm + "1"];
|
|
let weekN = String(Number(week1) + Number(props["quantityBillingPeriodService" + typeForm + "1"]) - 1);
|
|
let year = props["dateBilling" + typeForm].substr(2, 2);// date.getFullYear();
|
|
name = year + "_wks_" + week1 + "-" + weekN;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
function plotHeadingsTableInvoiceOrEstimate(context) {
|
|
context.font = 'bold 12px ' + nameFont;
|
|
context.fillText('DESCRIPTION', xPositionTableColumns[0] + 30, heightHeadTable);
|
|
context.fillText('QUANTITY', xPositionTableColumns[1] - 20, heightHeadTable);
|
|
context.fillText('UNIT COST [' + props.currency + ']', xPositionTableColumns[2] - 12, heightHeadTable);
|
|
context.fillText('SUBTOTAL', xPositionTableColumns[3] - 10, heightHeadTable);
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
{renderCanvases()}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default PageInvoiceOrEstimate; |