Implement tax handling for branches by adding tax percent to regions, updating product sell costs, and enhancing UI for tax summaries in BranchView
This commit is contained in:
@@ -82,6 +82,16 @@
|
||||
"title": "Erdbeben",
|
||||
"description": "Ein Erdbeben hat die Region {regionName} erschüttert."
|
||||
}
|
||||
,
|
||||
"taxes": {
|
||||
"title": "Steuern",
|
||||
"loading": "Steuerdaten werden geladen...",
|
||||
"total": "Gesamte Steuer",
|
||||
"table": {
|
||||
"region": "Region",
|
||||
"taxPercent": "Steuersatz"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"health": {
|
||||
@@ -177,7 +187,20 @@
|
||||
"inventory": "Inventar",
|
||||
"production": "Produktion",
|
||||
"storage": "Lager",
|
||||
"transport": "Transportmittel"
|
||||
"transport": "Transportmittel",
|
||||
"taxes": "Steuern"
|
||||
},
|
||||
"taxes": {
|
||||
"title": "Steuern",
|
||||
"loading": "Steuerdaten werden geladen...",
|
||||
"loadingError": "Fehler beim Laden der Steuerdaten: {error}",
|
||||
"retry": "Erneut laden",
|
||||
"noData": "Keine Steuerdaten verfügbar",
|
||||
"total": "Gesamte Steuer",
|
||||
"table": {
|
||||
"region": "Region",
|
||||
"taxPercent": "Steuersatz"
|
||||
}
|
||||
},
|
||||
"selection": {
|
||||
"title": "Niederlassungsauswahl",
|
||||
|
||||
@@ -172,6 +172,26 @@
|
||||
"four_horse_carriage": "Four-horse carriage",
|
||||
"raft": "Raft",
|
||||
"sailing_ship": "Sailing ship"
|
||||
},
|
||||
"tabs": {
|
||||
"director": "Director",
|
||||
"inventory": "Inventory",
|
||||
"production": "Production",
|
||||
"storage": "Storage",
|
||||
"transport": "Transport",
|
||||
"taxes": "Taxes"
|
||||
}
|
||||
,"taxes": {
|
||||
"title": "Taxes",
|
||||
"loading": "Loading tax data...",
|
||||
"loadingError": "Failed to load tax data: {error}",
|
||||
"retry": "Retry",
|
||||
"noData": "No tax data available",
|
||||
"total": "Total tax",
|
||||
"table": {
|
||||
"region": "Region",
|
||||
"taxPercent": "Tax %"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nobility": {
|
||||
@@ -270,6 +290,16 @@
|
||||
"details": {
|
||||
"title": "Child Details"
|
||||
}
|
||||
,
|
||||
"taxes": {
|
||||
"title": "Taxes",
|
||||
"loading": "Loading tax data...",
|
||||
"total": "Total tax",
|
||||
"table": {
|
||||
"region": "Region",
|
||||
"taxPercent": "Tax %"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,11 @@
|
||||
|
||||
<!-- Inventar / Verkauf -->
|
||||
<div v-else-if="activeTab === 'inventory'" class="branch-tab-pane">
|
||||
<!-- Tax summary for inventory/sales -->
|
||||
<div v-if="branchTaxes" class="branch-tax-summary">
|
||||
<strong>{{ $t('falukant.branch.taxes.total') }}:</strong>
|
||||
<span>{{ formatPercent(branchTaxes.total) }}</span>
|
||||
</div>
|
||||
<SaleSection
|
||||
:branchId="selectedBranch.id"
|
||||
:vehicles="vehicles"
|
||||
@@ -51,6 +56,11 @@
|
||||
:products="products"
|
||||
ref="productionSection"
|
||||
/>
|
||||
<!-- Tax summary for production -->
|
||||
<div v-if="branchTaxes" class="branch-tax-summary">
|
||||
<strong>{{ $t('falukant.branch.taxes.total') }}:</strong>
|
||||
<span>{{ formatPercent(branchTaxes.total) }}</span>
|
||||
</div>
|
||||
<RevenueSection
|
||||
:products="products"
|
||||
:calculateProductRevenue="calculateProductRevenue"
|
||||
@@ -60,6 +70,41 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Taxes Übersicht -->
|
||||
<div v-else-if="activeTab === 'taxes'" class="branch-tab-pane">
|
||||
<h3>{{ $t('falukant.branch.taxes.title') }}</h3>
|
||||
<div v-if="branchTaxesLoading">
|
||||
<p>{{ $t('falukant.branch.taxes.loading') }}</p>
|
||||
</div>
|
||||
<div v-else-if="branchTaxesError">
|
||||
<p>{{ $t('falukant.branch.taxes.loadingError', { error: branchTaxesError }) }}</p>
|
||||
<button @click="loadBranchTaxes">{{ $t('falukant.branch.taxes.retry') }}</button>
|
||||
</div>
|
||||
<div v-else-if="!branchTaxes || !branchTaxes.breakdown || branchTaxes.breakdown.length === 0">
|
||||
<p>{{ $t('falukant.branch.taxes.noData') }}</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p>
|
||||
<strong>{{ $t('falukant.branch.taxes.total') }}:</strong>
|
||||
<span>{{ formatCurrency(branchTaxes.total) }}</span>
|
||||
</p>
|
||||
<table class="taxes-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('falukant.branch.taxes.table.region') }}</th>
|
||||
<th>{{ $t('falukant.branch.taxes.table.taxPercent') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="r in branchTaxes.breakdown" :key="r.id">
|
||||
<td>{{ r.name }}</td>
|
||||
<td>{{ r.taxPercent }}%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lager -->
|
||||
<div v-else-if="activeTab === 'storage'" class="branch-tab-pane">
|
||||
<StorageSection :branchId="selectedBranch.id" ref="storageSection" />
|
||||
@@ -318,6 +363,7 @@ export default {
|
||||
tabs: [
|
||||
{ value: 'production', label: 'falukant.branch.tabs.production' },
|
||||
{ value: 'inventory', label: 'falukant.branch.tabs.inventory' },
|
||||
{ value: 'taxes', label: 'falukant.branch.tabs.taxes' },
|
||||
{ value: 'director', label: 'falukant.branch.tabs.director' },
|
||||
{ value: 'storage', label: 'falukant.branch.tabs.storage' },
|
||||
{ value: 'transport', label: 'falukant.branch.tabs.transport' },
|
||||
@@ -341,6 +387,9 @@ export default {
|
||||
vehicleIds: [],
|
||||
totalCost: null,
|
||||
},
|
||||
branchTaxes: null,
|
||||
branchTaxesLoading: false,
|
||||
branchTaxesError: null,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -401,6 +450,8 @@ export default {
|
||||
this.socket.on('transport_arrived', (data) => this.handleEvent({ event: 'transport_arrived', ...data }));
|
||||
this.socket.on('inventory_updated', (data) => this.handleEvent({ event: 'inventory_updated', ...data }));
|
||||
}
|
||||
// Load taxes for the initially selected branch (if any)
|
||||
await this.loadBranchTaxes();
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
@@ -416,6 +467,23 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
activeTab(newVal) {
|
||||
if (newVal === 'taxes') {
|
||||
this.loadBranchTaxes();
|
||||
}
|
||||
},
|
||||
selectedBranch: {
|
||||
handler(newBranch) {
|
||||
// if taxes tab is active, refresh when branch changes
|
||||
if (this.activeTab === 'taxes' && newBranch && newBranch.id) {
|
||||
this.loadBranchTaxes();
|
||||
}
|
||||
},
|
||||
deep: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async loadBranches() {
|
||||
try {
|
||||
@@ -446,6 +514,29 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async loadBranchTaxes() {
|
||||
if (!this.selectedBranch || !this.selectedBranch.id) {
|
||||
this.branchTaxes = null;
|
||||
this.branchTaxesError = null;
|
||||
this.branchTaxesLoading = false;
|
||||
return;
|
||||
}
|
||||
this.branchTaxesLoading = true;
|
||||
this.branchTaxesError = null;
|
||||
try {
|
||||
const res = await apiClient.get(`/api/falukant/branches/${this.selectedBranch.id}/taxes`);
|
||||
this.branchTaxes = res.data;
|
||||
} catch (err) {
|
||||
console.error('Failed to load branch taxes', err);
|
||||
// Try to surface a useful error message
|
||||
const remoteMsg = err?.response?.data?.error || err?.message || String(err);
|
||||
this.branchTaxes = null;
|
||||
this.branchTaxesError = remoteMsg;
|
||||
} finally {
|
||||
this.branchTaxesLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async onBranchSelected(newBranch) {
|
||||
this.selectedBranch = newBranch;
|
||||
// Branches neu laden, um das Wetter zu aktualisieren
|
||||
@@ -467,6 +558,9 @@ export default {
|
||||
this.$refs.revenueSection?.refresh && this.$refs.revenueSection.refresh();
|
||||
});
|
||||
|
||||
// load tax info for this branch
|
||||
this.loadBranchTaxes();
|
||||
|
||||
// Beim Initial-Laden sicherstellen, dass ein Tab-Inhalt sichtbar ist
|
||||
if (this.selectedBranch && !this.activeTab) {
|
||||
this.activeTab = 'director';
|
||||
@@ -501,6 +595,14 @@ export default {
|
||||
this.productPricesCache = prices;
|
||||
},
|
||||
|
||||
formatPercent(value) {
|
||||
if (value === null || value === undefined) return '—';
|
||||
// Ensure numeric and format with locale with max 2 fraction digits
|
||||
const num = typeof value === 'string' ? parseFloat(value) : Number(value);
|
||||
if (Number.isNaN(num)) return '—';
|
||||
return num.toLocaleString(undefined, { maximumFractionDigits: 2 }) + '%';
|
||||
},
|
||||
|
||||
async createBranch() {
|
||||
await this.loadBranches();
|
||||
// Nach dem Anlegen eines neuen Branches automatisch den
|
||||
@@ -933,10 +1035,17 @@ export default {
|
||||
formatMoney(amount) {
|
||||
if (amount == null) return '';
|
||||
try {
|
||||
return amount.toLocaleString(undefined, {
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0,
|
||||
});
|
||||
return this.formatCurrency(amount);
|
||||
} catch (e) {
|
||||
return String(amount);
|
||||
}
|
||||
},
|
||||
|
||||
formatCurrency(amount) {
|
||||
if (amount == null) return '';
|
||||
try {
|
||||
// Use euro currency formatting with locale-sensitive digits
|
||||
return Number(amount).toLocaleString(undefined, { style: 'currency', currency: 'EUR' });
|
||||
} catch (e) {
|
||||
return String(amount);
|
||||
}
|
||||
|
||||
@@ -97,9 +97,6 @@ export default {
|
||||
}
|
||||
return translation !== key ? translation : activity;
|
||||
},
|
||||
locale() {
|
||||
return window.navigator.language || 'en-US';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user