mirror of
https://github.com/InvoiceShelf/InvoiceShelf.git
synced 2026-09-03 22:01:00 +00:00
Add compound tax support (#753)
* test(taxes): cover compound_tax API semantics and validate it as boolean * feat(taxes): restore the compound tax toggle in tax type settings * feat(taxes): compute document-level compound tax amounts Two-pass recalculation: simple taxes on the discounted subtotal, compound taxes on the subtotal plus all simple taxes (v2 parity; branch order fixed -> compound -> inclusive back-out -> simple). Amounts now also recalculate on tax add/remove and on the tax-inclusive toggle, which previously never triggered recalculation. * feat(taxes): support compound taxes in per-item mode Per-item tax rows now carry the compound flag and charge compound taxes on the item's discounted base plus its simple taxes, through the same calcTaxAmount branch order as document-level taxes. The item row splits its tax sums into simple and compound so a compound row can never widen its own base. Also fixes two pre-existing bugs: the per-item tax dropdown read window.__taxTypes, which nothing ever assigned, so it always rendered empty (tax types are now fetched by the items table and passed down); and removing a tax row hard-zeroed the item's totals instead of re-syncing them. * fix(invoices): skip placeholder tax rows when persisting item taxes The document form keeps one empty placeholder tax row per item in per-item tax mode. Its empty name is nullified by the framework's empty-string middleware, so inserting it violated the NOT NULL constraint and turned every per-item save into a 500. The equivalent v2 guard keyed on a null amount, which the v3 stub (amount: 0) evades; keying on the missing tax_type_id catches it.
This commit is contained in:
@@ -92,6 +92,8 @@
|
||||
:currency="defaultCurrency"
|
||||
:item-validation-scope="itemValidationScope"
|
||||
:invoice-items="formData.items"
|
||||
:tax-types="availableTaxTypes"
|
||||
:can-add-tax="canAddTax"
|
||||
:store="store"
|
||||
:store-prop="storeProp"
|
||||
/>
|
||||
@@ -110,11 +112,15 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import draggable from 'vuedraggable'
|
||||
import DocumentItemRow from './DocumentItemRow.vue'
|
||||
import ItemModal from '@/scripts/features/company/items/components/ItemModal.vue'
|
||||
import { useUserStore } from '../../../stores/user.store'
|
||||
import { taxTypeService } from '../../../api/services/tax-type.service'
|
||||
import { ABILITIES } from '../../../config/abilities'
|
||||
import type { Currency } from '../../../types/domain/currency'
|
||||
import type { TaxType } from '../../../types/domain/tax'
|
||||
import type { DocumentFormData } from './use-document-calculations'
|
||||
|
||||
interface Props {
|
||||
@@ -134,6 +140,25 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
taxIncludedSetting: 'NO',
|
||||
})
|
||||
|
||||
const userStore = useUserStore()
|
||||
const availableTaxTypes = ref<TaxType[]>([])
|
||||
|
||||
const canAddTax = computed<boolean>(() => {
|
||||
return userStore.hasAbilities(ABILITIES.CREATE_TAX_TYPE)
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const response = await taxTypeService.list({
|
||||
limit: 'all',
|
||||
transaction_type: 'sales',
|
||||
})
|
||||
availableTaxTypes.value = response.data
|
||||
} catch {
|
||||
// Silently fail
|
||||
}
|
||||
})
|
||||
|
||||
const formData = computed<DocumentFormData>(() => {
|
||||
return props.store[props.storeProp] as DocumentFormData
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user