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(
(canvasRefs.current[indexCanvas] = ref)} width={widthA4} height={heightA4} style={{ border: '1px solid black', margin: '10px' }} />
); } 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 (
{renderCanvases()}
); }; export default PageInvoiceOrEstimate;