Merged in release/2022-07-22 (pull request #542)

release/2022-07-22

Approved-by: Patrick Fic
This commit is contained in:
Patrick Fic
2022-07-20 22:19:46 +00:00
13 changed files with 263 additions and 40 deletions

View File

@@ -27406,6 +27406,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>estimator</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>existing_jobs</name>
<definition_loaded>false</definition_loaded>
@@ -32606,6 +32627,48 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>systemnotes</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>usernotes</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node>

View File

@@ -94,7 +94,20 @@ export function BillDetailEditReturn({
<table style={{ tableLayout: "auto", width: "100%" }}>
<thead>
<tr>
<td></td>
<td>
<Checkbox
onChange={(e) => {
form.setFieldsValue({
billlines: form
.getFieldsValue()
.billlines.map((b) => ({
...b,
selected: e.target.checked,
})),
});
}}
/>
</td>
<td>{t("billlines.fields.line_desc")}</td>
<td>{t("billlines.fields.quantity")}</td>
<td>{t("billlines.fields.actual_price")}</td>

View File

@@ -67,39 +67,7 @@ export function BillsListTableComponent({
jobRO
}
/>
<Button
disabled={
record.is_credit_memo ||
record.vendorid === bodyshop.inhousevendorid ||
jobRO
}
onClick={() => {
setPartsOrderContext({
actions: {},
context: {
jobId: job.id,
vendorId: record.vendorid,
returnFromBill: record.id,
invoiceNumber: record.invoice_number,
linesToOrder: record.billlines.map((i) => {
return {
line_desc: i.line_desc,
// db_price: i.actual_price,
act_price: i.actual_price,
cost: i.actual_cost,
quantity: i.quantity,
joblineid: i.joblineid,
oem_partno: i.jobline && i.jobline.oem_partno,
part_type: i.jobline && i.jobline.part_type,
};
}),
isReturn: true,
},
});
}}
>
{t("bills.actions.return")}
</Button>
{record.isinhouse && (
<PrintWrapperComponent
templateObject={{

View File

@@ -115,7 +115,10 @@ export function JobLinesComponent({
sortOrder:
state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order,
ellipsis: true,
render: (text, record) => record.oem_partno,
render: (text, record) =>
`${record.oem_partno || ""} ${
record.alt_partno ? `(${record.alt_partno})` : ""
}`.trim(),
},
{
title: t("joblines.fields.op_code_desc"),
@@ -461,7 +464,12 @@ export function JobLinesComponent({
context: {
jobId: job.id,
job: job,
linesToOrder: selectedLines,
linesToOrder: selectedLines.map((l) => ({
...l,
oem_partno: `${l.oem_partno || ""} ${
l.alt_partno ? `(${l.alt_partno})` : ""
}`.trim(),
})),
},
});
@@ -477,7 +485,18 @@ export function JobLinesComponent({
setState({
...state,
filteredInfo: {
part_type: ["PAN","PAC","PAR","PAL","PAA","PAM","PAP","PAS","PASL","PAG"],
part_type: [
"PAN",
"PAC",
"PAR",
"PAL",
"PAA",
"PAM",
"PAP",
"PAS",
"PASL",
"PAG",
],
},
});
}}

View File

@@ -14,6 +14,7 @@ import { selectAllMedia } from "../../redux/media/media.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { CreateExplorerLinkForJob } from "../../utils/localmedia";
import DocumentsLocalUploadComponent from "../documents-local-upload/documents-local-upload.component";
import JobsLocalGalleryDownloadButton from "./jobs-documents-local-gallery.download";
import JobsDocumentsLocalGalleryReassign from "./jobs-documents-local-gallery.reassign.component";
import JobsDocumentsLocalGallerySelectAllComponent from "./jobs-documents-local-gallery.selectall.component";
@@ -78,6 +79,7 @@ export function JobsDocumentsLocalGallery({
</a>
<JobsDocumentsLocalGalleryReassign jobid={job.id} />
<JobsDocumentsLocalGallerySelectAllComponent jobid={job.id} />
<JobsLocalGalleryDownloadButton job={job} />
</Space>
<Card>
<DocumentsLocalUploadComponent

View File

@@ -0,0 +1,74 @@
import { Button } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import cleanAxios from "../../utils/CleanAxios";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectAllMedia } from "../../redux/media/media.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
allMedia: selectAllMedia,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsLocalGalleryDownloadButton);
export function JobsLocalGalleryDownloadButton({
bodyshop,
galleryImages,
allMedia,
job,
}) {
const { t } = useTranslation();
const [download, setDownload] = useState(null);
function downloadProgress(progressEvent) {
setDownload((currentDownloadState) => {
return {
downloaded: progressEvent.loaded || 0,
speed:
(progressEvent.loaded || 0) -
((currentDownloadState && currentDownloadState.downloaded) || 0),
};
});
}
const handleDownload = async () => {
const theDownloadedZip = await cleanAxios.post(
`${bodyshop.localmediaserverhttp}/jobs/download`,
{
jobid: job.id,
files: ((allMedia && allMedia[job.id]) || [])
.filter((i) => i.isSelected)
.map((i) => i.filename),
},
{
headers: { ims_token: bodyshop.localmediatoken },
responseType: "arraybuffer",
onDownloadProgress: downloadProgress,
}
);
setDownload(null);
standardMediaDownload(theDownloadedZip.data, job.ro_number);
};
return (
<Button loading={!!download} onClick={handleDownload}>
{t("documents.actions.download")}
</Button>
);
}
function standardMediaDownload(bufferData, filename) {
const a = document.createElement("a");
const url = window.URL.createObjectURL(new Blob([bufferData]));
a.href = url;
a.download = `${filename}.zip`;
a.click();
}

View File

@@ -264,6 +264,32 @@ export function JobsList({ bodyshop }) {
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
),
},
{
title: t("jobs.labels.estimator"),
dataIndex: "jobs.labels.estimator",
key: "jobs.labels.estimator",
ellipsis: true,
responsive: ["xl"],
filterSearch: true,
filters:
(jobs &&
jobs
.map((j) => `${j.est_ct_fn || ""} ${j.est_ct_ln || ""}`.trim())
.filter(onlyUnique)
.map((s) => {
return {
text: s || "N/A",
value: [s],
};
})) ||
[],
onFilter: (value, record) =>
value.includes(
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim()
),
render: (text, record) =>
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim(),
},
{
title: t("jobs.fields.comment"),
dataIndex: "comment",

View File

@@ -14,6 +14,7 @@ import { selectJobReadOnly } from "../../redux/application/application.selectors
import { setModalContext } from "../../redux/modals/modals.actions";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import { TemplateList } from "../../utils/TemplateConstants";
import useLocalStorage from "../../utils/useLocalStorage";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import NoteUpsertModal from "../note-upsert-modal/note-upsert-modal.container";
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
@@ -40,6 +41,8 @@ export function JobNotesComponent({
relatedRos,
}) {
const { t } = useTranslation();
const [filter, setFilter] = useLocalStorage("filter_job_notes_icons", null);
const Templates = TemplateList("job_special", {
ro_number,
});
@@ -50,6 +53,18 @@ export function JobNotesComponent({
dataIndex: "icons",
key: "icons",
width: 80,
filteredValue: filter?.icons || null,
filters: [
{
text: t("notes.labels.usernotes"),
value: false,
},
{
text: t("notes.labels.systemnotes"),
value: true,
},
],
onFilter: (value, record) => record.audit === value,
render: (text, record) => (
<span>
{record.critical ? (
@@ -131,6 +146,10 @@ export function JobNotesComponent({
},
];
const handleTableChange = (pagination, filters, sorter) => {
setFilter(filters);
};
return (
<div>
<LayoutFormRow>
@@ -166,6 +185,7 @@ export function JobNotesComponent({
columns={columns}
rowKey="id"
dataSource={data}
onChange={handleTableChange}
/>
</Card>
</div>

View File

@@ -276,6 +276,32 @@ export function JobsReadyList({ bodyshop }) {
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
),
},
{
title: t("jobs.labels.estimator"),
dataIndex: "jobs.labels.estimator",
key: "jobs.labels.estimator",
ellipsis: true,
responsive: ["xl"],
filterSearch: true,
filters:
(jobs &&
jobs
.map((j) => `${j.est_ct_fn || ""} ${j.est_ct_ln || ""}`.trim())
.filter(onlyUnique)
.map((s) => {
return {
text: s || "N/A",
value: [s],
};
})) ||
[],
onFilter: (value, record) =>
value.includes(
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim()
),
render: (text, record) =>
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim(),
},
{
title: t("jobs.fields.comment"),
dataIndex: "comment",

View File

@@ -40,6 +40,8 @@ export const QUERY_ALL_ACTIVE_JOBS = gql`
updated_at
ded_amt
suspended
est_ct_fn
est_ct_ln
}
}
`;
@@ -685,6 +687,7 @@ export const GET_JOB_BY_PK = gql`
line_ref
part_type
oem_partno
alt_partno
db_price
act_price
part_qty

View File

@@ -1616,6 +1616,7 @@
"duplicateconfirm": "Are you sure you want to duplicate this job? Some elements of this job will not be duplicated.",
"employeeassignments": "Employee Assignments",
"estimatelines": "Estimate Lines",
"estimator": "Estimator",
"existing_jobs": "Existing Jobs",
"federal_tax_amt": "Federal Taxes",
"gpdollars": "$ G.P.",
@@ -1919,7 +1920,9 @@
"labels": {
"addtorelatedro": "Add to Related ROs",
"newnoteplaceholder": "Add a note...",
"notetoadd": "Note to Add"
"notetoadd": "Note to Add",
"systemnotes": "System Notes",
"usernotes": "User Notes"
},
"successes": {
"create": "Note created successfully.",

View File

@@ -1616,6 +1616,7 @@
"duplicateconfirm": "",
"employeeassignments": "",
"estimatelines": "",
"estimator": "",
"existing_jobs": "Empleos existentes",
"federal_tax_amt": "",
"gpdollars": "",
@@ -1919,7 +1920,9 @@
"labels": {
"addtorelatedro": "",
"newnoteplaceholder": "Agrega una nota...",
"notetoadd": ""
"notetoadd": "",
"systemnotes": "",
"usernotes": ""
},
"successes": {
"create": "Nota creada con éxito.",

View File

@@ -1616,6 +1616,7 @@
"duplicateconfirm": "",
"employeeassignments": "",
"estimatelines": "",
"estimator": "",
"existing_jobs": "Emplois existants",
"federal_tax_amt": "",
"gpdollars": "",
@@ -1919,7 +1920,9 @@
"labels": {
"addtorelatedro": "",
"newnoteplaceholder": "Ajouter une note...",
"notetoadd": ""
"notetoadd": "",
"systemnotes": "",
"usernotes": ""
},
"successes": {
"create": "Remarque créée avec succès.",