aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/classes/item.js6
-rw-r--r--src/components/editors/invoice-headers-editor.js40
-rw-r--r--src/components/pickers/item-picker.js25
-rw-r--r--src/components/pickers/scss/item-picker.scss4
-rw-r--r--src/components/tables/invoice-item-table.js2
-rw-r--r--src/components/tables/invoice-summary.js7
-rw-r--r--src/components/tables/item-table.js2
-rw-r--r--src/components/tables/scss/summary.scss25
-rw-r--r--src/components/tables/scss/table.scss2
-rw-r--r--src/views/invoice/new.js17
-rw-r--r--src/views/invoice/scss/invoice.scss22
11 files changed, 136 insertions, 16 deletions
diff --git a/src/classes/item.js b/src/classes/item.js
index e9e692b..65e8fed 100644
--- a/src/classes/item.js
+++ b/src/classes/item.js
@@ -24,7 +24,7 @@ export const currency = value => c(value, {
decimal: '.',
seperator: ',',
precision: 2,
- symbol: '₹',
+ symbol: '₹ ',
});
export class Item {
@@ -74,8 +74,8 @@ export const editItem = (item, ok, fail) => {
.catch(err => fail());
}
-export const getDiscountValue = (item) => item.DiscountPercentage > 0
- ? currency(item.UnitPrice).multiply(item.Quantity).divide(100) : currency(0.00)
+export const getDiscountValue = (item) =>
+ currency(item.UnitPrice).multiply(item.Quantity).divide(100).multiply(item.DiscountPercentage)
export const getGSTValue = (item) => item.GSTPercentage > 0
? currency(item.UnitPrice).multiply(item.Quantity).subtract(getDiscountValue).divide(100).multiply(item.GSTPercentage) : currency(0.00)
diff --git a/src/components/editors/invoice-headers-editor.js b/src/components/editors/invoice-headers-editor.js
new file mode 100644
index 0000000..f71b319
--- /dev/null
+++ b/src/components/editors/invoice-headers-editor.js
@@ -0,0 +1,40 @@
+/* OpenBills-web - Web based libre billing software
+ * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@vidhukant.xyz>
+
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+import './scss/item-editor.scss';
+
+import { useState, useEffect } from 'react';
+
+const InvoiceHeadersEditor = ({roundOff, setRoundOff}) => {
+
+ return (
+ <>
+ <h1>Invoice Options:</h1>
+ <form onSubmit={(e) => e.preventDefault()}>
+ <label>
+ <input
+ type="checkbox"
+ onChange={() => setRoundOff(prev => !prev)}
+ checked={roundOff}/>
+ Round off the Total
+ </label>
+ </form>
+ </>
+ )
+}
+
+export default InvoiceHeadersEditor;
diff --git a/src/components/pickers/item-picker.js b/src/components/pickers/item-picker.js
index c486660..3029fb5 100644
--- a/src/components/pickers/item-picker.js
+++ b/src/components/pickers/item-picker.js
@@ -44,15 +44,18 @@ const ItemPicker = ({invoiceItems, addInvoiceItem}) => {
}
const handleInput = e => {
- const { name, value, type } = e.target;
- const val = type === "number" ? parseFloat(value) : value
+ const { name, value } = e.target;
setItem(prevItem => ({
...prevItem,
- [name]: type === "number" ? (isNaN(val) ? prevItem[name] : val) : val
+ [name]: `${value}`
}));
}
const validate = () => {
+ if (!isNumeric(item.Quantity) || !isNumeric(item.DiscountPercentage)
+ || !isNumeric(item.GSTPercentage) || !isNumeric(item.UnitPrice)) {
+ return false;
+ }
if (item.Id === null) {
return false;
}
@@ -68,7 +71,8 @@ const ItemPicker = ({invoiceItems, addInvoiceItem}) => {
if (item.MaxQuantity > 0 && item.Quantity > item.MaxQuantity) {
return false;
}
- if (item.DiscountPercentage > 100 || item.GSTPercentage > 100) {
+ if (item.DiscountPercentage > 100 || item.DiscountPercentage < 0
+ || item.GSTPercentage > 100 || item.GSTPercentage < 0) {
return false;
}
return true;
@@ -83,6 +87,8 @@ const ItemPicker = ({invoiceItems, addInvoiceItem}) => {
}
}
+ const isNumeric = (i) => !isNaN(i) && !isNaN(parseFloat(i));
+
// input elements are sorted on the basis of
// how likely they are going to be edited
return (
@@ -106,10 +112,10 @@ const ItemPicker = ({invoiceItems, addInvoiceItem}) => {
Quantity:
<input
type="number"
+ className={((!isNumeric(item.Quantity) || (item.MaxQuantity > 0 && item.Quantity > item.MaxQuantity) || (item.Quantity < item.MinQuantity))) ? "warning" : ""}
step="0.01"
value={item.Quantity}
name="Quantity"
- min={item.MinQuantity > 0 ? item.MinQuantity : 1}
max={item.MaxQuantity > 0 ? item.MaxQuantity : null}
onChange={handleInput} />
</label>
@@ -117,6 +123,7 @@ const ItemPicker = ({invoiceItems, addInvoiceItem}) => {
Price:
<input
type="number"
+ className={(!isNumeric(item.UnitPrice) || item.UnitPrice < 0) ? "warning" : ""}
step="0.01"
value={item.UnitPrice}
name="UnitPrice"
@@ -126,7 +133,10 @@ const ItemPicker = ({invoiceItems, addInvoiceItem}) => {
Discount %:
<input
type="number"
- step="0.01"
+ className={(!isNumeric(item.DiscountPercentage) || item.DiscountPercentage < 0 || item.DiscountPercentage > 100) ? "warning" : ""}
+ max="100"
+ min="0"
+ step="0.1"
value={item.DiscountPercentage}
name="DiscountPercentage"
onChange={handleInput} />
@@ -152,6 +162,9 @@ const ItemPicker = ({invoiceItems, addInvoiceItem}) => {
<input
type="number"
step="0.01"
+ className={(!isNumeric(item.GSTPercentage) || item.GSTPercentage < 0 || item.GSTPercentage > 100) ? "warning" : ""}
+ max="100"
+ min="0"
value={item.GSTPercentage}
name="GSTPercentage"
onChange={handleInput} />
diff --git a/src/components/pickers/scss/item-picker.scss b/src/components/pickers/scss/item-picker.scss
index b3e797a..63ddbcf 100644
--- a/src/components/pickers/scss/item-picker.scss
+++ b/src/components/pickers/scss/item-picker.scss
@@ -92,4 +92,8 @@
color: $fgColorAlt;
background-color: $warningColor;
}
+
+ input.warning {
+ border-color: $warningColor;
+ }
}
diff --git a/src/components/tables/invoice-item-table.js b/src/components/tables/invoice-item-table.js
index d008ebc..865b326 100644
--- a/src/components/tables/invoice-item-table.js
+++ b/src/components/tables/invoice-item-table.js
@@ -38,7 +38,7 @@ const ItemTable = ({items, setItems, isInterstate, sum}) => {
*/
return (
<>
- <table>
+ <table className={"item-table"}>
<thead>
<tr>
<th>S. No</th>
diff --git a/src/components/tables/invoice-summary.js b/src/components/tables/invoice-summary.js
index 7018d50..a5c594f 100644
--- a/src/components/tables/invoice-summary.js
+++ b/src/components/tables/invoice-summary.js
@@ -15,16 +15,17 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+import './scss/summary.scss';
import { currency } from '../../classes/item';
-const InvoiceSummary = ({sum}) => {
+const InvoiceSummary = ({sum, roundOff}) => {
const totalRoundedOff = currency(sum.Amount !== undefined ? Math.round(sum.Amount.value) : 0.00);
- const roundedOffDiff = sum.Amount !== undefined ? sum.Amount.subtract(totalRoundedOff) : currency(0.00);
+ const roundedOffDiff = sum.Amount !== undefined && roundOff ? sum.Amount.subtract(totalRoundedOff) : currency(0.00);
return (
<>
<h1>Summary:</h1>
- <table>
+ <table className={"summary-table"}>
<tbody>
{sum.UnitPrice !== undefined &&
<tr>
diff --git a/src/components/tables/item-table.js b/src/components/tables/item-table.js
index 5a405a5..2fb7210 100644
--- a/src/components/tables/item-table.js
+++ b/src/components/tables/item-table.js
@@ -39,7 +39,7 @@ const ItemTable = (props) => {
}
return (
- <table>
+ <table className={"item-table"}>
<thead>
<tr>
<th>S. No</th>
diff --git a/src/components/tables/scss/summary.scss b/src/components/tables/scss/summary.scss
new file mode 100644
index 0000000..d5714f9
--- /dev/null
+++ b/src/components/tables/scss/summary.scss
@@ -0,0 +1,25 @@
+/* OpenBills-web - Web based libre billing software
+ * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@vidhukant.xyz>
+
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+@import "colors";
+
+table.summary-table {
+ min-width: 25rem;
+ tr:hover {
+ background-color: rgba($thColor, 0.4);
+ }
+}
diff --git a/src/components/tables/scss/table.scss b/src/components/tables/scss/table.scss
index 0bb5a48..d201a49 100644
--- a/src/components/tables/scss/table.scss
+++ b/src/components/tables/scss/table.scss
@@ -17,7 +17,7 @@
@import "colors";
-table {
+table.item-table {
width: 90%;
margin: auto;
diff --git a/src/views/invoice/new.js b/src/views/invoice/new.js
index be180cf..cacf8d3 100644
--- a/src/views/invoice/new.js
+++ b/src/views/invoice/new.js
@@ -15,10 +15,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+import "./scss/invoice.scss";
+
import ClientPicker from '../../components/pickers/client-picker';
import ItemPicker from '../../components/pickers/item-picker';
import ItemTable from '../../components/tables/invoice-item-table';
import InvoiceSummary from '../../components/tables/invoice-summary';
+import HeadersEditor from '../../components/editors/invoice-headers-editor';
import { InvoiceClient } from '../../classes/client';
import { calcSum, currency } from '../../classes/item';
@@ -29,6 +32,7 @@ const NewInvoicePage = () => {
const [client, setClient] = useState(new InvoiceClient());
const [shippingAddressId, setShippingAddressId] = useState(-1);
const [items, setItems] = useState([]);
+ const [roundOffTotal, setRoundOffTotal] = useState(true); //TODO: load from config
//const [isInterstate, setIsInterstate] = useState(false);
const isInterstate = false; // temporary
const [sum, setSum] = useState({
@@ -58,7 +62,18 @@ const NewInvoicePage = () => {
setItems={setItems}
isInterstate={isInterstate}
sum={sum} />
- <InvoiceSummary sum={sum} />
+ <div className={"two-col"}>
+ <div>
+ <HeadersEditor
+ roundOff={roundOffTotal}
+ setRoundOff={setRoundOffTotal} />
+ </div>
+ <div>
+ <InvoiceSummary
+ sum={sum}
+ roundOff={roundOffTotal} />
+ </div>
+ </div>
</>
);
}
diff --git a/src/views/invoice/scss/invoice.scss b/src/views/invoice/scss/invoice.scss
new file mode 100644
index 0000000..32607b1
--- /dev/null
+++ b/src/views/invoice/scss/invoice.scss
@@ -0,0 +1,22 @@
+/* OpenBills-web - Web based libre billing software
+ * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@vidhukant.xyz>
+
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+.two-col {
+ width: 100%;
+ display: flex;
+ justify-content: space-between;
+}