import { Component, createMemo, For, Show, useContext } from "solid-js"; import { formatAmount, InvoiceData } from "./SwissInvoice"; import { autoAnimate } from "~/directives/autoAnimate"; import { LocalStoreContext, LocalStoreObject, Position, POSITION_TYPE_AGILE, StoreContext, StoreObject, } from "~/stores"; import Big from "big.js"; import Markdown from "./Markdown"; export const calculateAgileQuantity = ( hoursPerStoryPoint = 0, riskFactor = 0, minPoints = 0, maxPoints = 0 ) => { if (minPoints > maxPoints) { maxPoints = minPoints; } const minHours = minPoints * hoursPerStoryPoint; const maxHours = maxPoints * hoursPerStoryPoint; const minWeighted = new Big(-1).plus(riskFactor).abs().mul(minHours); const maxWeighted = new Big(riskFactor).mul(maxHours); const quantity = minWeighted.plus(maxWeighted).round().toNumber(); return quantity; }; const getQuantity = (position: Position, state: StoreObject) => { let quantity = position.quantity; if (position.type === POSITION_TYPE_AGILE) { const min = position.agilePointsMin || 0; let max = position.agilePointsMax || 0; const agileRiskFactor = position.agileRiskFactor != null ? position.agileRiskFactor : state.agileRiskFactor; quantity = calculateAgileQuantity( state.agileHoursPerStoryPoint, agileRiskFactor, min, max ); } return quantity; }; const calculatePrice = (position: Position, state: StoreObject) => { const itemPrice = position.itemPrice != null ? position.itemPrice : state.defaultItemPrice; return new Big(itemPrice).mul(getQuantity(position, state)).toNumber(); }; const calculatePriceAfterDiscount = ( position: Position, state: StoreObject ) => { if (position.fixedDiscountPrice != null) { return position.fixedDiscountPrice; } return calculatePrice(position, state); }; export const calculatePositionsTax = ( positionsPrice: number, localState: LocalStoreObject ) => { return new Big(localState.vatRate).mul(positionsPrice).round(2, 0).toNumber(); }; export const calculatePositionsPrice = ( positions: Position[], state: StoreObject ) => { const result = positions .reduce((acc, next) => { if (!next.enabled) { return acc; } return acc.plus(calculatePriceAfterDiscount(next, state)); }, new Big(0)) .toNumber(); return result; }; const Positions: Component<{ positions: Position[]; invoiceData: InvoiceData; }> = (props) => { const [state] = useContext(StoreContext)!; const [localState] = useContext(LocalStoreContext)!; autoAnimate; const positions = createMemo(function () { return props.positions.filter((p) => p.enabled); }); return ( {(position, idx) => { const hasTwoRows = createMemo( () => !!position.description || position.fixedDiscountPrice != null ); return ( <> ); }} 0}>
Pos. Bezeichnung Menge Einzelpreis Gesamtpreis
{position.number || idx() + 1} {position.name} {getQuantity(position, state)} {formatAmount( position.itemPrice != null ? position.itemPrice : state.defaultItemPrice )}{" "} CHF {formatAmount(calculatePrice(position, state))} CHF
{formatAmount(position.fixedDiscountPrice!)} CHF
Summe {formatAmount(props.invoiceData.amountBeforeTax)} CHF
Mehrwertsteuer {new Big(localState.vatRate).mul(100).toNumber()} % {formatAmount(props.invoiceData.tax)} CHF
Gesamtbetrag {formatAmount(props.invoiceData.amount)} CHF
); }; export default Positions;