Basic IIF rendering with test component for BOD-83
This commit is contained in:
@@ -1,143 +1,49 @@
|
|||||||
import { Editor } from "@tinymce/tinymce-react";
|
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import React, { useState } from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { EmailSettings } from "../../utils/TemplateConstants";
|
|
||||||
import {
|
|
||||||
endLoading,
|
|
||||||
startLoading,
|
|
||||||
} from "../../redux/application/application.actions";
|
|
||||||
import { setEmailOptions } from "../../redux/email/email.actions";
|
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import { auth } from "../../firebase/firebase.utils";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
|
||||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
load: () => dispatch(startLoading()),
|
|
||||||
endload: () => dispatch(endLoading()),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(function Test({ setEmailOptions, load, endload, bodyshop }) {
|
)(function Test({ bodyshop }) {
|
||||||
const [state, setState] = useState(temp);
|
const handle = async () => {
|
||||||
|
console.log(
|
||||||
const handleEditorChange = (content, editor) => {
|
"await auth.currentUser.getIdToken(true)",
|
||||||
setState(content);
|
await auth.currentUser.getIdToken(true)
|
||||||
|
);
|
||||||
|
const response = await axios.post(
|
||||||
|
"/accounting/iif/receivables",
|
||||||
|
{ jobId: "661dd1d5-bf06-426f-8bd2-bd9e41de8eb1" },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
console.log("handle -> result", response);
|
||||||
|
const url = window.URL.createObjectURL(new Blob([response.data]));
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = url;
|
||||||
|
link.setAttribute(
|
||||||
|
"download",
|
||||||
|
response.headers.filename || "receivables.iif"
|
||||||
|
); //or any other extension
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button onClick={handle}>Hit with Header.</button>
|
||||||
onClick={() => {
|
|
||||||
axios
|
|
||||||
.post("/render", {
|
|
||||||
view: state,
|
|
||||||
context: {
|
|
||||||
people: ["Yehuda Katz", "Alan Johnson", "Charles Jolley"],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((r) => {
|
|
||||||
var newWin = window.open(
|
|
||||||
"url",
|
|
||||||
"windowName",
|
|
||||||
"height=300,width=300"
|
|
||||||
);
|
|
||||||
newWin.document.write(r.data);
|
|
||||||
});
|
|
||||||
}}>
|
|
||||||
TinyMCE
|
|
||||||
</button>
|
|
||||||
<Editor
|
|
||||||
value={state}
|
|
||||||
apiKey='f3s2mjsd77ya5qvqkee9vgh612cm6h41e85efqakn2d0kknk'
|
|
||||||
init={{
|
|
||||||
height: 500,
|
|
||||||
//menubar: false,
|
|
||||||
encoding: "raw",
|
|
||||||
extended_valid_elements: "span",
|
|
||||||
|
|
||||||
plugins: [
|
|
||||||
"advlist autolink lists link image charmap print preview anchor",
|
|
||||||
"searchreplace visualblocks code fullscreen",
|
|
||||||
"insertdatetime media table paste code help wordcount",
|
|
||||||
],
|
|
||||||
toolbar:
|
|
||||||
"undo redo | formatselect | bold italic backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | help",
|
|
||||||
}}
|
|
||||||
onEditorChange={handleEditorChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={() =>
|
|
||||||
setEmailOptions({
|
|
||||||
messageOptions: {
|
|
||||||
to: "patrickwf@gmail.com",
|
|
||||||
Subject: "TODO FIX ME",
|
|
||||||
},
|
|
||||||
template: {
|
|
||||||
name: "appointment_reminder",
|
|
||||||
variables: { id: "2b42336f-b8de-4f04-a053-d6bff034d384" },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}>
|
|
||||||
Set email config.
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() =>
|
|
||||||
setEmailOptions({
|
|
||||||
messageOptions: {
|
|
||||||
from: {
|
|
||||||
name: bodyshop.shopname || EmailSettings.fromNameDefault,
|
|
||||||
address: EmailSettings.fromAddress,
|
|
||||||
},
|
|
||||||
to: "patrickwf@gmail.com",
|
|
||||||
replyTo: bodyshop.email,
|
|
||||||
Subject: "TODO FIX ME",
|
|
||||||
},
|
|
||||||
template: {
|
|
||||||
name: "parts_order_confirmation",
|
|
||||||
variables: { id: "6fea31e9-ea85-4c89-ac56-6f9cc84531fe" },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}>
|
|
||||||
Parts Order
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const temp = `<div style="font-family: Arial, Helvetica, sans-serif;">
|
|
||||||
<p style="text-align: center;"><span>→<span> This is a full-featured editor demo. </span>Please explore! ←</span></p>
|
|
||||||
<p style="text-align: center;"> </p>
|
|
||||||
<h2 style="text-align: center;"><span>TinyMCE is the world's most customizable, and flexible, rich text editor.</span></h2>
|
|
||||||
<p style="text-align: center;"><span><strong> A featherweight download, TinyMCE can handle any challenge you throw at it. </strong></span></p>
|
|
||||||
<p style="text-align: center;"> </p>
|
|
||||||
<p> </p>
|
|
||||||
<table style="border-collapse: collapse; width: 85%; height: 86px; border-color: initial; border-style: solid; margin-left: auto; margin-right: auto;">
|
|
||||||
<tbody>
|
|
||||||
<tr style="height: 22px;">
|
|
||||||
<td style="width: 25%; text-align: center; padding: 7px; height: 22px;"><span>🛠 50+ Plugins</span></td>
|
|
||||||
<td style="width: 25%; text-align: center; padding: 7px; height: 22px;"><span>💡 Premium Support</span></td>
|
|
||||||
<td style="width: 25%; text-align: center; padding: 7px; height: 22px;"><span>🖍 Custom Skins</span></td>
|
|
||||||
<td style="width: 25%; text-align: center; padding: 7px; height: 22px;"><span>⚙ Full API Access</span></td>
|
|
||||||
</tr>
|
|
||||||
<tr style="height: 21px; display: none;">
|
|
||||||
<td style="height: 21px; display: none;"><span>{{#each people}}</span></td>
|
|
||||||
</tr>
|
|
||||||
<tr style="height: 22px;">
|
|
||||||
<td style="width: 25%; text-align: center; padding: 7px; height: 22px; border-style: solid; border-width: 1px;"><span>{{this}}</span></td>
|
|
||||||
<td style="width: 25%; text-align: center; padding: 7px; height: 22px; border-style: solid; border-width: 1px;"><span>{{this}}</span></td>
|
|
||||||
<td style="width: 25%; text-align: center; padding: 7px; height: 22px; border-style: solid; border-width: 1px;"><span>{{this}}</span></td>
|
|
||||||
<td style="width: 25%; text-align: center; padding: 7px; height: 22px; border-style: solid; border-width: 1px;"><span>{{this}}</span></td>
|
|
||||||
</tr>
|
|
||||||
<tr style="height: 21px; display: none;">
|
|
||||||
<td style="height: 21px;"><span>{{/each}}</span></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>`;
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export function JobsCloseSaveButton({
|
|||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
console.log("suspense", suspenseAmount);
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
|
|||||||
@@ -576,7 +576,7 @@
|
|||||||
"rate_la3": "LA3 Rate",
|
"rate_la3": "LA3 Rate",
|
||||||
"rate_la4": "LA4 Rate",
|
"rate_la4": "LA4 Rate",
|
||||||
"rate_laa": "Aluminum Rate",
|
"rate_laa": "Aluminum Rate",
|
||||||
"rate_lab": "Labor Rate",
|
"rate_lab": "Body Rate",
|
||||||
"rate_lad": "Diagnostic Rate",
|
"rate_lad": "Diagnostic Rate",
|
||||||
"rate_lae": "Electrical Rate",
|
"rate_lae": "Electrical Rate",
|
||||||
"rate_laf": "Frame Rate",
|
"rate_laf": "Frame Rate",
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"cloudinary": "^1.21.0",
|
"cloudinary": "^1.21.0",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"cors": "2.8.5",
|
"cors": "2.8.5",
|
||||||
|
"dinero.js": "^1.8.1",
|
||||||
"dotenv": "8.2.0",
|
"dotenv": "8.2.0",
|
||||||
"express": "^4.16.4",
|
"express": "^4.16.4",
|
||||||
"express-sslify": "^1.2.0",
|
"express-sslify": "^1.2.0",
|
||||||
|
|||||||
12
server.js
12
server.js
@@ -31,10 +31,18 @@ var sendEmail = require("./sendemail.js");
|
|||||||
app.post("/sendemail", sendEmail.sendEmail);
|
app.post("/sendemail", sendEmail.sendEmail);
|
||||||
|
|
||||||
//Test route to ensure Express is responding.
|
//Test route to ensure Express is responding.
|
||||||
app.get("/test", function (req, res) {
|
app.get("/test", async function (req, res) {
|
||||||
res.status(200).send();
|
res.status(200).send("OK");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.post("/test", async function (req, res) {
|
||||||
|
res.status(200).send("OK");
|
||||||
|
});
|
||||||
|
|
||||||
|
//Invoicing-IIF
|
||||||
|
const accountingIIF = require("./server/accounting/iif/iif");
|
||||||
|
app.post("/accounting/iif/receivables", accountingIIF.receivables);
|
||||||
|
|
||||||
//Cloudinary Media Paths
|
//Cloudinary Media Paths
|
||||||
var media = require("./server/media/media");
|
var media = require("./server/media/media");
|
||||||
app.post("/media/sign", media.createSignedUploadURL);
|
app.post("/media/sign", media.createSignedUploadURL);
|
||||||
|
|||||||
58
server/accounting/iif/iif-receivables.js
Normal file
58
server/accounting/iif/iif-receivables.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||||
|
const path = require("path");
|
||||||
|
require("dotenv").config({
|
||||||
|
path: path.resolve(
|
||||||
|
process.cwd(),
|
||||||
|
`.env.${process.env.NODE_ENV || "development"}`
|
||||||
|
),
|
||||||
|
});
|
||||||
|
const queries = require("../../graphql-client/queries");
|
||||||
|
|
||||||
|
exports.default = async (req, res) => {
|
||||||
|
const BearerToken = req.headers.authorization;
|
||||||
|
const { jobId } = req.body;
|
||||||
|
|
||||||
|
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||||
|
headers: {
|
||||||
|
Authorization: BearerToken,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await client
|
||||||
|
.setHeaders({ Authorization: BearerToken })
|
||||||
|
.request(queries.QUERY_JOBS_FOR_RECEIVABLES_EXPORT, { id: jobId });
|
||||||
|
|
||||||
|
const response = [];
|
||||||
|
response.push(TRNS_HEADER);
|
||||||
|
response.push(generateInvoiceHeader(result.jobs_by_pk));
|
||||||
|
response.push(END_TRNS);
|
||||||
|
|
||||||
|
res.setHeader("Content-type", "application/octet-stream");
|
||||||
|
res.setHeader("Content-disposition", "attachment; filename=file.txt");
|
||||||
|
res.setHeader("filename", `${result.jobs_by_pk.ro_number}-RECEIVABLES.iif`);
|
||||||
|
res.send(response.join("\n"));
|
||||||
|
} catch (error) {
|
||||||
|
console.log("error", error);
|
||||||
|
res.status(400).send(JSON.stringify(error));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const TRNS_HEADER = `!TRNS TRNSID TRNSTYPE DATE ACCNT NAME CLASS AMOUNT DOCNUM MEMO CLEAR TOPRINT NAMEISTAXABLE ADDR1 ADDR2 ADDR3 ADDR4 DUEDATE TERMS OTHER1 PONUM
|
||||||
|
!SPL SPLID TRNSTYPE DATE ACCNT NAME CLASS AMOUNT DOCNUM MEMO CLEAR QNTY PRICE INVITEM PAYMETH TAXABLE VALADJ SERVICEDATE OTHER2 EXTRA
|
||||||
|
!ENDTRNS`;
|
||||||
|
|
||||||
|
const generateInvoiceHeader = (job) =>
|
||||||
|
`TRNS INVOICE ${new Date(job.date_invoiced).getMonth() + 1}/${new Date(
|
||||||
|
job.date_invoiced
|
||||||
|
).getDate()}/${new Date(
|
||||||
|
job.date_invoiced
|
||||||
|
).getFullYear()} Accounts Receivable GUO DA Acct.# ${job.ownr_id}:${
|
||||||
|
job.ro_number
|
||||||
|
} 0100 ${job.clm_total} ${job.ro_number} N N Y GUO DA Acct.# ${job.ownr_id}:${
|
||||||
|
job.ro_number
|
||||||
|
} ${job.ownr_addr1} ${job.ownr_city} ${job.ownr_st} ${job.ownr_zip} `;
|
||||||
|
const generateInvoiceLine = (line) =>
|
||||||
|
`SPL INVOICE 05/21/2020 Sales:Total Labour:Sales, Body Shop Labour 0100 -572.60 114519 Labor Body N 572.60 Total Labour:4015 Y N `;
|
||||||
|
|
||||||
|
const END_TRNS = `ENDTRNS`;
|
||||||
1
server/accounting/iif/iif.js
Normal file
1
server/accounting/iif/iif.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
exports.receivables = require("./iif-receivables").default
|
||||||
@@ -1,10 +1,17 @@
|
|||||||
const GraphQLClient = require("graphql-request").GraphQLClient;
|
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
require("dotenv").config({ path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || 'development'}`) });
|
require("dotenv").config({
|
||||||
|
path: path.resolve(
|
||||||
|
process.cwd(),
|
||||||
|
`.env.${process.env.NODE_ENV || "development"}`
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
//TODO May need to use a different client that includes caching of resources.
|
//TODO May need to use a different client that includes caching of resources.
|
||||||
exports.client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
exports.client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||||
headers: {
|
headers: {
|
||||||
"x-hasura-admin-secret": process.env.HASURA_ADMIN_SECRET
|
"x-hasura-admin-secret": process.env.HASURA_ADMIN_SECRET,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
exports.unauthclient = new GraphQLClient(process.env.GRAPHQL_ENDPOINT);
|
||||||
|
|||||||
@@ -39,3 +39,27 @@ mutation UPDATE_MESSAGE($msid: String!, $fields: messages_set_input!) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports.QUERY_JOBS_FOR_RECEIVABLES_EXPORT = `
|
||||||
|
query QUERY_JOBS_FOR_RECEIVABLES_EXPORT($id: uuid!){
|
||||||
|
jobs_by_pk(id: $id) {
|
||||||
|
id
|
||||||
|
job_totals
|
||||||
|
date_invoiced
|
||||||
|
ro_number
|
||||||
|
clm_total
|
||||||
|
invoice_allocation
|
||||||
|
ownerid
|
||||||
|
ownr_addr1
|
||||||
|
ownr_addr2
|
||||||
|
ownr_zip
|
||||||
|
ownr_city
|
||||||
|
ownr_st
|
||||||
|
|
||||||
|
}
|
||||||
|
bodyshops{
|
||||||
|
id
|
||||||
|
md_responsibility_centers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|||||||
@@ -803,6 +803,11 @@ dicer@^0.3.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
streamsearch "0.1.2"
|
streamsearch "0.1.2"
|
||||||
|
|
||||||
|
dinero.js@^1.8.1:
|
||||||
|
version "1.8.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/dinero.js/-/dinero.js-1.8.1.tgz#775a647629b4195af9d02f46e9b7fa1fd81e906d"
|
||||||
|
integrity sha512-AQ09MDKonkGUrhBZZFx4tPTVcVJuHJ0VEA73LvcBoBB2eQSi1DbapeXj4wnUUpx1hVnPdyev1xPNnNMGy/Au0g==
|
||||||
|
|
||||||
doctrine@^3.0.0:
|
doctrine@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
|
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
|
||||||
|
|||||||
Reference in New Issue
Block a user