IO-2327 posting bills test caes
This commit is contained in:
194
client/cypress/e2e/posting-bills/posting-bills.cy.js
Normal file
194
client/cypress/e2e/posting-bills/posting-bills.cy.js
Normal file
@@ -0,0 +1,194 @@
|
||||
import uniqueId from "lodash/uniqueId";
|
||||
|
||||
describe(
|
||||
"Billing job parts orders",
|
||||
{
|
||||
defaultCommandTimeout: 5000,
|
||||
},
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
cy.visit("/manage");
|
||||
|
||||
cy.get("body").then(($body) => {
|
||||
if ($body.text().includes("Login")) {
|
||||
// Log in
|
||||
cy.get('[data-cy="username"]').type("john@imex.dev");
|
||||
cy.get('[data-cy="password"]').type("john123");
|
||||
cy.get('[data-cy="sign-in-button"]').click();
|
||||
}
|
||||
|
||||
cy.get(".ant-table-tbody")
|
||||
.should("be.visible")
|
||||
.find("tr")
|
||||
.should("not.have.class", "ant-table-placeholder");
|
||||
|
||||
cy.get(".ant-table-row")
|
||||
.not(':contains("Open")')
|
||||
.first()
|
||||
.find("a")
|
||||
.first()
|
||||
.click();
|
||||
|
||||
cy.url().should("include", "/manage/jobs");
|
||||
|
||||
// Go to repair data tab
|
||||
cy.get('[data-cy="tab-partssublet"]').should("be.visible").click();
|
||||
});
|
||||
});
|
||||
|
||||
it("receives a part bill", () => {
|
||||
// Find the first row in the parts order
|
||||
cy.get('[data-cy="part-orders-table"]')
|
||||
.find(".ant-table-tbody")
|
||||
.find("> tr:not(.ant-table-measure-row)")
|
||||
.as("orders-table")
|
||||
.should("not.have.class", "ant-table-placeholder");
|
||||
|
||||
cy.get("@orders-table")
|
||||
.first()
|
||||
.should("be.visible")
|
||||
.find('[data-cy="receive-bill-button"]')
|
||||
.click();
|
||||
|
||||
// fill out form
|
||||
// data-cy="bill-form-invoice"
|
||||
cy.get('[data-cy="bill-form-invoice"]').type(uniqueId());
|
||||
|
||||
cy.get("#bill-form-date").click();
|
||||
cy.get('[title="2023-06-22"]').should("be.visible").click();
|
||||
|
||||
// get table
|
||||
cy.get('[data-cy="bill-line-table"]').each(($row) => {
|
||||
// get retail amount
|
||||
cy.wrap($row)
|
||||
.find('[data-cy="bill-line-actual-price"]')
|
||||
.invoke("val")
|
||||
.then((p) => {
|
||||
const vendorPercentOff = 0.2;
|
||||
|
||||
// get cost - vendor's percent off
|
||||
const cost = p - p * vendorPercentOff;
|
||||
cy.get('[data-cy="bill-form-bill-total"]').type(p);
|
||||
|
||||
cy.wrap($row).find('[data-cy="bill-line-actual-cost"]').type(cost);
|
||||
});
|
||||
});
|
||||
|
||||
// Click save
|
||||
cy.get('[data-cy="bill-form-save-button"]')
|
||||
.should("not.be.disabled")
|
||||
.click();
|
||||
|
||||
cy.get(".ant-notification-notice-message").contains(
|
||||
"Invoice added successfully."
|
||||
);
|
||||
});
|
||||
|
||||
it("posts bill directly", () => {
|
||||
cy.get('[data-cy="bills-post-button"]').should("be.visible").click();
|
||||
|
||||
// Add New Line
|
||||
cy.get('[data-cy="bill-line-add-button"]')
|
||||
.should("not.be.disabled")
|
||||
.click();
|
||||
// Select Vendor
|
||||
cy.antdSelect("bill-vendor");
|
||||
// Select Line
|
||||
cy.antdSelect("bill-line", "-- Not On Estimate --");
|
||||
// Fill the Form
|
||||
cy.get('[data-cy="bill-form-invoice"]').type(uniqueId("DBP"));
|
||||
|
||||
cy.get("#bill-form-date").click();
|
||||
cy.get('[title="2023-06-22"]').should("be.visible").click();
|
||||
|
||||
// get table
|
||||
cy.get('[data-cy="bill-line-table"]').each(($row) => {
|
||||
// get retail amount
|
||||
cy.wrap($row)
|
||||
.find('[data-cy="bill-line-actual-price"]')
|
||||
.invoke("val")
|
||||
.then((p) => {
|
||||
const vendorPercentOff = 0.2;
|
||||
|
||||
// get cost - vendor's percent off
|
||||
const cost = p - p * vendorPercentOff;
|
||||
cy.get('[data-cy="bill-form-bill-total"]').type(p);
|
||||
|
||||
cy.wrap($row).find('[data-cy="bill-line-actual-cost"]').type(cost);
|
||||
});
|
||||
});
|
||||
|
||||
// Click save
|
||||
cy.get('[data-cy="bill-form-save-button"]')
|
||||
.should("not.be.disabled")
|
||||
.click();
|
||||
|
||||
cy.get(".ant-notification-notice-message").contains(
|
||||
"Invoice added successfully."
|
||||
);
|
||||
});
|
||||
|
||||
it("posts a bill with save and new", () => {
|
||||
cy.get('[data-cy="bills-post-button"]').should("be.visible").click();
|
||||
|
||||
// Add New Line
|
||||
cy.get('[data-cy="bill-line-add-button"]')
|
||||
.should("not.be.disabled")
|
||||
.click();
|
||||
// Select Vendor
|
||||
cy.antdSelect("bill-vendor");
|
||||
// Select Line
|
||||
cy.antdSelect("bill-line", "-- Not On Estimate --");
|
||||
// Fill the Form
|
||||
cy.get('[data-cy="bill-form-invoice"]').type(uniqueId("DBP"));
|
||||
|
||||
cy.get("#bill-form-date").click();
|
||||
cy.get('[title="2023-06-22"]').should("be.visible").click();
|
||||
|
||||
// get table
|
||||
cy.get('[data-cy="bill-line-table"]').each(($row) => {
|
||||
// get retail amount
|
||||
cy.wrap($row)
|
||||
.find('[data-cy="bill-line-actual-price"]')
|
||||
.invoke("val")
|
||||
.then((p) => {
|
||||
const vendorPercentOff = 0.2;
|
||||
|
||||
// get cost - vendor's percent off
|
||||
const cost = p - p * vendorPercentOff;
|
||||
cy.get('[data-cy="bill-form-bill-total"]').type(p);
|
||||
|
||||
cy.wrap($row).find('[data-cy="bill-line-actual-cost"]').type(cost);
|
||||
});
|
||||
});
|
||||
|
||||
// Click save
|
||||
cy.get('[data-cy="bill-form-savenew-button"]')
|
||||
.should("not.be.disabled")
|
||||
.click();
|
||||
|
||||
cy.get(".ant-notification-notice-message").contains(
|
||||
"Invoice added successfully."
|
||||
);
|
||||
});
|
||||
|
||||
it("uploads a document to a bill", () => {
|
||||
cy.get('[data-cy="bills-table"]')
|
||||
.find(".ant-table-tbody")
|
||||
.find("> tr:not(.ant-table-measure-row)")
|
||||
.as("bills-table")
|
||||
.should("not.have.class", "ant-table-placeholder");
|
||||
|
||||
cy.get("@bills-table")
|
||||
.first()
|
||||
.should("be.visible")
|
||||
.find('[data-cy="edit-bill-button"]')
|
||||
.click();
|
||||
|
||||
cy.location("search").should("include", "billid");
|
||||
cy.get('[data-cy="bill-edit-form"]')
|
||||
.find(".ant-upload #bill-document-upload")
|
||||
.selectFile("job.json", { force: true });
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -50,7 +50,7 @@ Cypress.on("uncaught:exception", (err, runnable) => {
|
||||
return false;
|
||||
});
|
||||
|
||||
Cypress.Commands.add("antdSelect", (selector) => {
|
||||
Cypress.Commands.add("antdSelect", (selector, filter) => {
|
||||
cy.get(`.ant-select-${selector} > .ant-select-selector`).click();
|
||||
cy.get(`.ant-select-${selector} .ant-select-selection-search input`)
|
||||
.invoke("attr", "id")
|
||||
@@ -59,6 +59,7 @@ Cypress.Commands.add("antdSelect", (selector) => {
|
||||
cy.get(dropDownSelector)
|
||||
.next()
|
||||
.find(".ant-select-item-option-content")
|
||||
.not(`:contains("${filter}")`)
|
||||
.first()
|
||||
.click();
|
||||
});
|
||||
|
||||
5912
client/job.json
Normal file
5912
client/job.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import {
|
||||
DELETE_BILL_LINE,
|
||||
INSERT_NEW_BILL_LINES,
|
||||
UPDATE_BILL_LINE
|
||||
UPDATE_BILL_LINE,
|
||||
} from "../../graphql/bill-lines.queries";
|
||||
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
@@ -200,6 +200,7 @@ export function BillDetailEditcontainer({
|
||||
}
|
||||
/>
|
||||
<Form
|
||||
data-cy="bill-edit-form"
|
||||
form={form}
|
||||
onFinish={handleFinish}
|
||||
initialValues={transformData(data)}
|
||||
|
||||
@@ -362,11 +362,16 @@ function BillEnterModalContainer({
|
||||
{t("bills.labels.generatepartslabel")}
|
||||
</Checkbox>
|
||||
<Button onClick={handleCancel}>{t("general.actions.cancel")}</Button>
|
||||
<Button loading={loading} onClick={() => form.submit()}>
|
||||
<Button
|
||||
data-cy="bill-form-save-button"
|
||||
loading={loading}
|
||||
onClick={() => form.submit()}
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
{billEnterModal.context && billEnterModal.context.id ? null : (
|
||||
<Button
|
||||
data-cy="bill-form-savenew-button"
|
||||
type="primary"
|
||||
loading={loading}
|
||||
onClick={() => {
|
||||
|
||||
@@ -177,6 +177,7 @@ export function BillFormComponent({
|
||||
]}
|
||||
>
|
||||
<VendorSearchSelect
|
||||
className="ant-select-bill-vendor"
|
||||
disabled={disabled}
|
||||
options={vendorAutoCompleteOptions}
|
||||
onSelect={handleVendorSelect}
|
||||
@@ -249,7 +250,10 @@ export function BillFormComponent({
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<Input disabled={disabled || disableInvNumber} />
|
||||
<Input
|
||||
data-cy="bill-form-invoice"
|
||||
disabled={disabled || disableInvNumber}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bills.fields.date")}
|
||||
@@ -261,7 +265,7 @@ export function BillFormComponent({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<FormDatePicker disabled={disabled} />
|
||||
<FormDatePicker id="bill-form-date" disabled={disabled} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bills.fields.is_credit_memo")}
|
||||
@@ -303,6 +307,7 @@ export function BillFormComponent({
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
data-cy="bill-form-bill-total"
|
||||
label={t("bills.fields.total")}
|
||||
name="total"
|
||||
rules={[
|
||||
@@ -456,19 +461,20 @@ export function BillFormComponent({
|
||||
}}
|
||||
>
|
||||
<Upload.Dragger
|
||||
id="bill-image-upload"
|
||||
multiple={true}
|
||||
name="logo"
|
||||
beforeUpload={() => false}
|
||||
listType="picture"
|
||||
>
|
||||
<>
|
||||
<div>
|
||||
<p className="ant-upload-drag-icon">
|
||||
<UploadOutlined />
|
||||
</p>
|
||||
<p className="ant-upload-text">
|
||||
Click or drag files to this area to upload.
|
||||
</p>
|
||||
</>
|
||||
</div>
|
||||
</Upload.Dragger>
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import {
|
||||
Button, Form,
|
||||
Button,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Table,
|
||||
Tooltip
|
||||
Tooltip,
|
||||
} from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -79,6 +80,7 @@ export function BillEnterModalLinesComponent({
|
||||
),
|
||||
formInput: (record, index) => (
|
||||
<BillLineSearchSelect
|
||||
className="ant-select-bill-line"
|
||||
disabled={disabled}
|
||||
options={lineData}
|
||||
style={{ width: "100%", minWidth: "10rem" }}
|
||||
@@ -194,6 +196,7 @@ export function BillEnterModalLinesComponent({
|
||||
},
|
||||
formInput: (record, index) => (
|
||||
<CurrencyInput
|
||||
data-cy="bill-line-actual-price"
|
||||
min={0}
|
||||
disabled={disabled}
|
||||
onBlur={(e) => {
|
||||
@@ -241,6 +244,7 @@ export function BillEnterModalLinesComponent({
|
||||
},
|
||||
formInput: (record, index) => (
|
||||
<CurrencyInput
|
||||
data-cy="bill-line-actual-cost"
|
||||
min={0}
|
||||
disabled={disabled}
|
||||
controls={false}
|
||||
@@ -572,6 +576,7 @@ export function BillEnterModalLinesComponent({
|
||||
return (
|
||||
<>
|
||||
<Table
|
||||
data-cy="bill-line-table"
|
||||
components={{
|
||||
body: {
|
||||
cell: EditableCell,
|
||||
@@ -587,6 +592,7 @@ export function BillEnterModalLinesComponent({
|
||||
/>
|
||||
<Form.Item>
|
||||
<Button
|
||||
data-cy="bill-line-add-button"
|
||||
disabled={disabled}
|
||||
onClick={() => {
|
||||
add();
|
||||
|
||||
@@ -54,7 +54,10 @@ export function BillsListTableComponent({
|
||||
const recordActions = (record, showView = false) => (
|
||||
<Space wrap>
|
||||
{showView && (
|
||||
<Button onClick={() => handleOnRowClick(record)}>
|
||||
<Button
|
||||
onClick={() => handleOnRowClick(record)}
|
||||
data-cy="edit-bill-button"
|
||||
>
|
||||
<EditFilled />
|
||||
</Button>
|
||||
)}
|
||||
@@ -178,6 +181,7 @@ export function BillsListTableComponent({
|
||||
{job && job.converted ? (
|
||||
<>
|
||||
<Button
|
||||
data-cy="bills-post-button"
|
||||
onClick={() => {
|
||||
setBillEnterContext({
|
||||
actions: { refetch: billsQuery.refetch },
|
||||
@@ -217,6 +221,7 @@ export function BillsListTableComponent({
|
||||
}
|
||||
>
|
||||
<Table
|
||||
data-cy="bills-table"
|
||||
loading={billsQuery.loading}
|
||||
scroll={{
|
||||
x: true, // y: "50rem"
|
||||
|
||||
@@ -35,6 +35,7 @@ export function DocumentsLocalUploadComponent({
|
||||
|
||||
return (
|
||||
<Upload.Dragger
|
||||
id="bill-document-upload"
|
||||
multiple={true}
|
||||
fileList={fileList}
|
||||
onChange={(f) => {
|
||||
|
||||
@@ -148,6 +148,7 @@ export function PartsOrderListTableComponent({
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
<Button
|
||||
data-cy="receive-bill-button"
|
||||
disabled={
|
||||
(jobRO ? !record.return : jobRO) ||
|
||||
record.vendor.id === bodyshop.inhousevendorid
|
||||
@@ -478,6 +479,7 @@ export function PartsOrderListTableComponent({
|
||||
{selectedPartsOrderRecord && rowExpander(selectedPartsOrderRecord)}
|
||||
</Drawer>
|
||||
<Table
|
||||
data-cy="part-orders-table"
|
||||
loading={billsQuery.loading}
|
||||
scroll={{
|
||||
x: true,
|
||||
|
||||
@@ -7,7 +7,16 @@ const { Option } = Select;
|
||||
//To be used as a form element only.
|
||||
|
||||
const VendorSearchSelect = (
|
||||
{ value, onChange, options, onSelect, disabled, preferredMake, showPhone },
|
||||
{
|
||||
value,
|
||||
onChange,
|
||||
options,
|
||||
onSelect,
|
||||
disabled,
|
||||
preferredMake,
|
||||
showPhone,
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const [option, setOption] = useState(value);
|
||||
@@ -43,6 +52,7 @@ const VendorSearchSelect = (
|
||||
optionFilterProp="name"
|
||||
onSelect={onSelect}
|
||||
disabled={disabled || false}
|
||||
{...rest}
|
||||
>
|
||||
{favorites
|
||||
? favorites.map((o) => (
|
||||
|
||||
@@ -312,7 +312,7 @@ export function JobsDetailPage({
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane
|
||||
tab={
|
||||
<span>
|
||||
<span data-cy="tab-partssublet">
|
||||
<ToolFilled />
|
||||
{t("menus.jobsdetail.partssublet")}
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user