diff --git a/admin/src/Membership/MemberBoxSpans.jsx b/admin/src/Membership/MemberBoxSpans.jsx
index ead1cd04f..1b140ae75 100644
--- a/admin/src/Membership/MemberBoxSpans.jsx
+++ b/admin/src/Membership/MemberBoxSpans.jsx
@@ -1,4 +1,4 @@
-import React from "react";
+import React, { useEffect, useState } from "react";
import "react-day-picker/lib/style.css";
import { Link } from "react-router-dom";
import CollectionTable from "../Components/CollectionTable";
@@ -11,113 +11,117 @@ import { get } from "../gateway";
import { confirmModal } from "../message";
import MembershipPeriodsInput from "./MembershipPeriodsInput";
-class MemberBoxSpans extends React.Component {
- constructor(props) {
- super(props);
- this.collection = new Collection({
- type: Span,
- url: `/membership/member/${props.match.params.member_id}/spans`,
- pageSize: 0,
- includeDeleted: true,
- });
- this.state = { items: [], pending_labaccess_days: "?" };
- this.pending_actions = get({
- url: `/membership/member/${props.match.params.member_id}/pending_actions`,
- }).then((r) => {
- const sum_pending_labaccess_days = r.data.reduce((acc, value) => {
- if (value.action.action === ADD_LABACCESS_DAYS)
- return acc + value.action.value;
- return acc;
- }, 0);
- this.setState({
- pending_labaccess_days: sum_pending_labaccess_days,
+const MemberBoxSpans = (props) => {
+ const collection = new Collection({
+ type: Span,
+ url: `/membership/member/${props.match.params.member_id}/spans`,
+ pageSize: 0,
+ includeDeleted: true,
+ });
+
+ const [pendingLabAccessDays, setPendingLabAccessDays] = useState("?");
+
+ useEffect(() => {
+ // Fetch pending actions
+ const fetchPendingActions = async () => {
+ const response = await get({
+ url: `/membership/member/${props.match.params.member_id}/pending_actions`,
});
- });
- }
+ const sumPendingLabAccessDays = response.data.reduce(
+ (acc, value) => {
+ if (value.action.action === ADD_LABACCESS_DAYS)
+ return acc + value.action.value;
+ return acc;
+ },
+ 0,
+ );
+ setPendingLabAccessDays(sumPendingLabAccessDays);
+ };
+
+ fetchPendingActions();
- componentDidMount() {
- this.unsubscribe = this.collection.subscribe(({ items }) => {
- this.setState({ items });
+ // Subscribe to collection
+ const unsubscribe = collection.subscribe(() => {
+ // No need to handle the subscription data since it's unused
});
- }
- componentWillUnmount() {
- this.unsubscribe();
- }
+ // Cleanup on component unmount
+ return () => {
+ unsubscribe();
+ };
+ }, [props.match.params.member_id]);
- render() {
- const deleteItem = (item) =>
- confirmModal(item.deleteConfirmMessage())
- .then(() => item.del())
- .then(
- () => this.collection.fetch(),
- () => null,
- );
+ const deleteItem = (item) =>
+ confirmModal(item.deleteConfirmMessage())
+ .then(() => item.del())
+ .then(
+ () => collection.fetch(),
+ () => null,
+ );
- return (
-
-
Utskick
-
-
-
-
- | Created |
-
-
- |
-
-
- | Status |
- {Message.statusText(message)} |
-
-
- | Sent |
-
-
- |
-
-
- | Recipient |
- {message.recipient} |
-
-
- | Template Used |
- {message.template} |
-
-
-
-
-
-
-
-
-
+ return (
+
+
Utskick
+
+
+
+
+ | Created |
+
+
+ |
+
+
+ | Status |
+ {Message.statusText(message)} |
+
+
+ | Sent |
+
+
+ |
+
+
+ | Recipient |
+ {message.recipient} |
+
+
+ | Template Used |
+ {message.template} |
+
+
+
- );
- }
-}
-export default withRouter(MessageShow);
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/admin/src/Sales/AccountingBox.jsx b/admin/src/Sales/AccountingBox.jsx
index 5e4a55a95..df5a761d5 100644
--- a/admin/src/Sales/AccountingBox.jsx
+++ b/admin/src/Sales/AccountingBox.jsx
@@ -1,34 +1,23 @@
import React from "react";
import { NavItem } from "../nav";
-import { withRouter } from "react-router";
-class AccountingBox extends React.Component {
- constructor(props) {
- super(props);
- }
+export default function AccountingBox({ children }) {
+ return (
+
+
Bokföring
- render() {
- return (
-
-
Bokföring
+
+ Exportera
+
+ Produkter
+
+ Konton
+
+ Kostnadsställen
+
+
-
-
- Exportera
-
-
- Produkter
-
- Konton
-
- Kostnadsställen
-
-
-
- {this.props.children}
-
- );
- }
-}
-
-export default withRouter(AccountingBox);
+ {children}
+
+ );
+}
\ No newline at end of file
diff --git a/admin/src/Sales/AccountingProduct.js b/admin/src/Sales/AccountingProduct.js
index f926488eb..3425fa625 100644
--- a/admin/src/Sales/AccountingProduct.js
+++ b/admin/src/Sales/AccountingProduct.js
@@ -1,4 +1,4 @@
-import React from "react";
+import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import Select from "react-select";
import * as _ from "underscore";
@@ -6,125 +6,73 @@ import CollectionTable from "../Components/CollectionTable";
import Currency from "../Components/Currency";
import SearchBox from "../Components/SearchBox";
import Collection from "../Models/Collection";
-import CollectionNavigation from "../Models/CollectionNavigation";
import Product from "../Models/Product";
import ProductAccountsCostCenters from "../Models/ProductAccountsCostCenters";
import { get } from "../gateway";
import { showError } from "../message";
-const filterOptions_account = (items_account, options_account) => {
- const current = new Set(items_account.map((i) => i.id));
- return options_account.filter((o) => !current.has(o.id));
-};
-
-const filterOptions_cost_center = (items_cost_center, options_cost_center) => {
- const current = new Set(items_cost_center.map((i) => i.id));
- return options_cost_center.filter((o) => !current.has(o.id));
-};
-
-const updateOptions_account = (options_account) => (prevState) => {
- let options = [
- {
- account: "No modification",
- id: 0,
- description: "Do not change account",
- },
- ].concat(filterOptions_account(prevState.items_account, options_account));
- return {
- showOptions_account: options,
- options_account,
- };
-};
-
-const updateOptions_cost_center = (options_cost_center) => (prevState) => {
- let options = [
- {
- cost_center: "No modification",
- id: 0,
- description: "Do not change cost center",
- },
- ].concat(
- filterOptions_cost_center(
- prevState.items_cost_center,
- options_cost_center,
- ),
- );
- return {
- showOptions_cost_center: options,
- options_cost_center,
- };
-};
-
-class AccountingProduct extends CollectionNavigation {
- constructor(props) {
- super(props);
- const { search, page } = this.state;
- this.collection = new Collection({
- type: Product,
- url: "/webshop/product",
- expand: "product_accounting",
- search: search,
- page: page,
- pageSize: 0,
- filter_out_key: "type",
- filter_out_value: "debit",
- includeDeleted: true,
- });
- this.state = {
- categories: null,
- items_account: [],
- options_account: [],
- selectedOption_account: null,
- items_cost_center: [],
- options_cost_center: [],
- selectedOption_cost_center: null,
- accounts: null,
- cost_centers: null,
- selected_product_id: [],
- latest_selected_row_index: null,
- };
-
- this.product = new Product();
- this.product_accounts_cost_centers = new ProductAccountsCostCenters();
-
+const AccountingProduct = () => {
+ const [categories, setCategories] = useState(null);
+ const [transactionAccount, setTransactionAccount] = useState(null);
+ const [transactionCostCenter, setTransactionCostCenter] = useState(null);
+ const [selectedOptionAccount, setSelectedOptionAccount] = useState(null);
+ const [showOptionsAccount, setShowOptionsAccount] = useState([]);
+ const [selectedOptionCostCenter, setSelectedOptionCostCenter] =
+ useState(null);
+ const [showOptionsCostCenter, setShowOptionsCostCenter] = useState([]);
+ const [selectedProductId, setSelectedProductId] = useState([]);
+
+ const collection = new Collection({
+ type: Product,
+ url: "/webshop/product",
+ expand: "product_accounting",
+ page: 1,
+ pageSize: 0,
+ filter_out_key: "type",
+ filter_out_value: "debit",
+ includeDeleted: true,
+ });
+
+ useEffect(() => {
+ // Fetch categories
get({ url: "/webshop/category", params: { page_size: 0 } })
- .then(
- (data) => {
- const categories = _.reduce(
- data.data,
- (obj, item) => {
- obj[item.id] = item.name;
- return obj;
- },
- {},
- );
- this.setState({ categories });
- },
- () => null,
- )
+ .then((data) => {
+ const fetchedCategories = _.reduce(
+ data.data,
+ (obj, item) => {
+ obj[item.id] = item.name;
+ return obj;
+ },
+ {},
+ );
+ setCategories(fetchedCategories);
+ })
.catch((error) => {
showError("
Failed to find categories
" + error.message);
});
- get({
- url: "/webshop/transaction_account",
- params: { page_size: 0 },
- })
- .then(
- (data) => {
- const transaction_account = _.reduce(
- data.data,
- (obj, item) => {
- obj[item.id] = item.account;
- return obj;
+ // Fetch transaction accounts
+ get({ url: "/webshop/transaction_account", params: { page_size: 0 } })
+ .then((data) => {
+ const fetchedAccounts = _.reduce(
+ data.data,
+ (obj, item) => {
+ obj[item.id] = item.account;
+ return obj;
+ },
+ {},
+ );
+ setTransactionAccount(fetchedAccounts);
+ setShowOptionsAccount(
+ [
+ {
+ account: "No modification",
+ id: 0,
+ description: "Do not change account",
},
- {},
- );
- this.setState({ transaction_account });
- this.setState(updateOptions_account(data.data));
- },
- () => null,
- )
+ ].concat(data.data),
+ );
+ })
.catch((error) => {
showError(
"
Failed to find transaction accounts
" +
@@ -132,401 +80,179 @@ class AccountingProduct extends CollectionNavigation {
);
});
+ // Fetch transaction cost centers
get({
url: "/webshop/transaction_cost_center",
params: { page_size: 0 },
})
- .then(
- (data) => {
- const transaction_cost_center = _.reduce(
- data.data,
- (obj, item) => {
- obj[item.id] = item.cost_center;
- return obj;
+ .then((data) => {
+ const fetchedCostCenters = _.reduce(
+ data.data,
+ (obj, item) => {
+ obj[item.id] = item.cost_center;
+ return obj;
+ },
+ {},
+ );
+ setTransactionCostCenter(fetchedCostCenters);
+ setShowOptionsCostCenter(
+ [
+ {
+ cost_center: "No modification",
+ id: 0,
+ description: "Do not change cost center",
},
- {},
- );
- this.setState({ transaction_cost_center });
- this.setState(updateOptions_cost_center(data.data));
- },
- () => null,
- )
+ ].concat(data.data),
+ );
+ })
.catch((error) => {
showError(
"
Failed to find transaction cost centers
" +
error.message,
);
});
- }
-
- selectOptionAccount(account) {
- this.setState({ selectedOption_account: account });
- }
-
- selectOptionCostCenter(cost_center) {
- this.setState({ selectedOption_cost_center: cost_center });
- }
-
- deserialize(x) {
- return x;
- }
-
- changeColor(activate = true, table_index) {
- const row = document.getElementsByTagName("tr");
- table_index.forEach(colorRows);
-
- function colorRows(id) {
- if (activate) {
- row[id + 1].style.backgroundColor = "#ddd";
- } else {
- row[id + 1].style.backgroundColor = "";
- }
- }
- }
+ }, []);
- onSave(account, cost_center) {
- const productIds = this.state.selected_product_id;
-
- productIds.forEach((product_id) => {
- get({ url: "/webshop/accounting", params: { page_size: 0 } })
- .then((data) => {
+ const onSave = (account, costCenter) => {
+ selectedProductId.forEach((productId) => {
+ get({ url: "/webshop/accounting", params: { page_size: 0 } }).then(
+ (data) => {
const accountings = data.data;
- const current_accounting = accountings.filter(
- (p) => p.product_id === product_id,
+ const currentAccounting = accountings.find(
+ (p) => p.product_id === productId,
);
- const accounting_element_debit =
- new ProductAccountsCostCenters(
- current_accounting.filter(
- (p) => p.type === "debit",
- )[0],
- );
- accounting_element_debit.product_id = product_id;
- accounting_element_debit.type = "debit";
- accounting_element_debit.account_id = 1;
- accounting_element_debit.fraction = 100;
- accounting_element_debit.save().then(() => {
- accounting_element_debit.reset();
- });
- const accounting_element_credit =
- new ProductAccountsCostCenters(
- current_accounting.filter(
- (p) => p.type === "credit",
- )[0],
- );
- accounting_element_credit.product_id = product_id;
- accounting_element_credit.type = "credit";
- accounting_element_credit.fraction = 100;
+
+ const accountingElementCredit =
+ new ProductAccountsCostCenters(currentAccounting);
+ accountingElementCredit.product_id = productId;
+ accountingElementCredit.type = "credit";
+ accountingElementCredit.fraction = 100;
if (account && account.id !== 0) {
- accounting_element_credit.account_id = account.id;
+ accountingElementCredit.account_id = account.id;
}
- if (cost_center && cost_center.id !== 0) {
- accounting_element_credit.cost_center_id =
- cost_center.id;
+ if (costCenter && costCenter.id !== 0) {
+ accountingElementCredit.cost_center_id = costCenter.id;
}
- return accounting_element_credit;
- })
- .then((accounting_element_credit) => {
- accounting_element_credit.save().then(() => {
- accounting_element_credit.reset();
- this.collection.fetch();
+
+ return accountingElementCredit.save().then(() => {
+ collection.fetch();
});
- return accounting_element_credit;
- });
+ },
+ );
});
- if (this.state.selected_product_id.length > 0) {
- const table_index = [];
- this.state.selected_product_id.forEach((product_id) => {
- table_index.push(
- this.collection.items
- .map(function (e) {
- return e.saved.id;
- })
- .indexOf(product_id),
- );
- });
- this.changeColor(false, table_index);
- }
-
- this.setState({ selected_product_id: [] });
- this.setState({ latest_selected_row_index: null });
- }
+ setSelectedProductId([]);
+ };
- updateAddSelectedProductId(current_product_ids, element) {
- if (Array.isArray(element)) {
- this.setState({
- selected_product_id: current_product_ids.concat(element),
- });
+ const setSelectedRow = (event, element) => {
+ // const tableIndex = collection.items.findIndex((item) => item.id === element.id);
+ if (selectedProductId.includes(element.id)) {
+ setSelectedProductId((prev) =>
+ prev.filter((id) => id !== element.id),
+ );
} else {
- this.setState({
- selected_product_id: current_product_ids.concat([element]),
- });
- }
- }
-
- updateRemoveSelectedProductId(current_product_ids, element) {
- this.setState({
- selected_product_id: current_product_ids.filter(
- (item) => item !== element,
- ),
- });
- }
-
- updateResetSelectedProductId(element = null, table_index = []) {
- if (this.state.selected_product_id.length > 0) {
- const table_index = [];
- this.state.selected_product_id.forEach((product_id) => {
- table_index.push(
- this.collection.items
- .map(function (e) {
- return e.saved.id;
- })
- .indexOf(product_id),
- );
- });
- this.changeColor(false, table_index);
+ setSelectedProductId((prev) => [...prev, element.id]);
}
- this.setState({ selected_product_id: [element] });
- this.changeColor(true, [table_index]);
- }
-
- updateLatestSelectedRowIndex(element) {
- this.setState({ latest_selected_row_index: element });
- }
+ };
- setSelectedRow(evt, element) {
- const table_index = this.collection.items
- .map(function (e) {
- return e.saved.id;
- })
- .indexOf(element.id);
- const selected_product_id = this.state.selected_product_id;
- const latest_selected_row_index = this.state.latest_selected_row_index;
+ return (
+
+
+
Hantera produkter för bokföring
+
+ På denna sida kan du ange och ta bort konton och
+ kostnadsställen för produkter. Markera en eller flera
+ produkter i listan och välj vilket konto och kostnadsställe
+ du vill applicera.
+
+
- if (selected_product_id.indexOf(element.id) > -1) {
- this.changeColor(false, [table_index]);
- this.updateRemoveSelectedProductId(selected_product_id, element.id);
- } else {
- if (evt.shiftKey) {
- if (latest_selected_row_index === null) {
- this.changeColor(true, [table_index]);
- this.updateAddSelectedProductId(
- selected_product_id,
- element.id,
- );
- } else {
- const new_product_id = [];
- const new_table_index = [];
- if (table_index > latest_selected_row_index) {
- for (
- let i = latest_selected_row_index + 1;
- i <= table_index;
- i++
- ) {
- if (
- selected_product_id.indexOf(
- this.collection.items[i].id,
- ) === -1
- ) {
- new_product_id.push(
- this.collection.items[i].id,
- );
- new_table_index.push(i);
- }
+