Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions frontend/src/components/ExpenseClaimItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<span>{{ claimDates }}</span>
<span class="whitespace-pre"> &middot; </span>
<span class="whitespace-nowrap">
{{ formatCurrency(props.doc.total_claimed_amount, currency) }}
{{ formatCurrency(props.doc.total_claimed_amount, props.doc.currency) }}
</span>
</div>
</div>
Expand All @@ -33,7 +33,6 @@ import { computed, inject } from "vue"
import ListItem from "@/components/ListItem.vue"
import ExpenseIcon from "@/components/icons/ExpenseIcon.vue"

import { getCompanyCurrency } from "@/data/currencies"
import { formatCurrency } from "@/utils/formatters"

const dayjs = inject("$dayjs")
Expand Down Expand Up @@ -99,7 +98,6 @@ const claimDates = computed(() => {
}
})

const currency = computed(() => getCompanyCurrency(props.doc.company))

const approvalStatus = computed(() => {
return props.doc.approval_status === "Draft" ? "Pending" : props.doc.approval_status
Expand Down
46 changes: 46 additions & 0 deletions frontend/src/components/ExpensesTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ import CustomIonModal from "@/components/CustomIonModal.vue"
import { claimTypesByID } from "@/data/claims"
import { formatCurrency } from "@/utils/formatters"

import { updateCurrencyLabels, updateBaseFieldsAmount } from "@/composables/useCurrencyConversion"

const props = defineProps({
expenseClaim: {
type: Object,
Expand Down Expand Up @@ -243,4 +245,48 @@ watch(
}
}
)

watch(
() => expensesTableFields.data,
(fields) => {
if (!fields) return

updateCurrencyLabels({
formFields: fields,
doc: props.expenseClaim,
baseFields: ["base_amount", "base_sanctioned_amount"],
transactionFields: ["amount", "sanctioned_amount"],
})
},
{ immediate: true }
)

watch(
() => [expenseItem.value.amount, expenseItem.value.sanctioned_amount],
() => {
if (expenseItem.value) {
updateBaseFieldsAmount({
doc: expenseItem.value,
fields: ['amount', 'sanctioned_amount'],
exchangeRate: props.expenseClaim.exchange_rate,
});
}
}
);

watch(
() => props.expenseClaim.exchange_rate,
(exchangeRate) => {
if (props.expenseClaim.expenses) {
props.expenseClaim.expenses.forEach(row => {
updateBaseFieldsAmount({
doc:row,
fields:['amount', 'sanctioned_amount'],
exchangeRate: exchangeRate
});
});
}
}
);

</script>
22 changes: 15 additions & 7 deletions frontend/src/components/Link.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ const searchText = ref("")
const value = computed({
get: () => props.modelValue,
set: (val) => {
const newVal = (val && typeof val === "object" && val.value !== undefined) ? val.value : val
const newVal =
val && typeof val === "object" && val.value !== undefined
? val.value
: val
emit("update:modelValue", newVal || "")
},
})
Expand Down Expand Up @@ -72,20 +75,20 @@ const reloadOptions = (searchTextVal) => {
params: {
txt: searchTextVal,
doctype: props.doctype,
filters: props.filters
filters: props.filters,
},
})
options.reload()
}

const handleQueryUpdate = debounce((newQuery) => {
const val = newQuery || ""
const val = newQuery || ""

if (val === "" && props.modelValue) return
if (val === "" && props.modelValue) return

if (searchText.value === val) return
searchText.value = val
reloadOptions(val)
if (searchText.value === val) return
searchText.value = val
reloadOptions(val)
}, 300)

watch(
Expand All @@ -96,4 +99,9 @@ watch(
},
{ immediate: true }
)

watch(
() => props.filters,
() => reloadOptions(''),
)
</script>
61 changes: 61 additions & 0 deletions frontend/src/composables/useCurrencyConversion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { ref, watch } from "vue"
import { getCompanyCurrency, currencyPrecision } from "@/data/currencies"

const flt = (value, precision) => {
const num = parseFloat(value) || 0;
const targetPrecision = precision !== undefined ? precision : (currencyPrecision.data || 2);
return parseFloat(num.toFixed(targetPrecision));
};

export function updateCurrencyLabels({ formFields, doc, baseFields = [], transactionFields = []}) {
if (!formFields || !doc) return
const companyCurrency = ref("")
// fetch company currency initially or when company changes
const fetchCompanyCurrency = async () => {
if (!doc.company) return
companyCurrency.value = await getCompanyCurrency(doc.company)
}

const currencyFields = new Set([...baseFields, ...transactionFields])
const updateLabels = () => {
if (!companyCurrency.value) return

formFields.forEach((field) => {
if (!field?.fieldname) return
if (!currencyFields.has(field.fieldname)) return

if (!field._original_label && field.label) {
field._original_label = field.label.replace(/\([^\)]*\)/g, "").trim()
}
if (baseFields.includes(field.fieldname)) {
field.label = `${field._original_label} (${companyCurrency.value})`
field.hidden = doc.currency === companyCurrency.value
}
if (transactionFields.includes(field.fieldname)) {
field.label = `${field._original_label} (${doc.currency})`
}
})
}

// update labels
watch(
() => [doc.company, doc.currency],
async () => {
await fetchCompanyCurrency()
updateLabels()
},
{ immediate: true }
)

return { updateLabels, companyCurrency }
}

// function to update base currency fields data
export function updateBaseFieldsAmount({doc, fields, exchangeRate}) {
if (!doc) return;
const exchange_rate = flt(exchangeRate || doc.exchange_rate || 1, 9);
fields.forEach(f => {
const val = flt(flt(doc[f]) * exchange_rate);
doc["base_" + f] = val;
});
}
10 changes: 10 additions & 0 deletions frontend/src/data/currencies.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,13 @@ export function getCompanyCurrencySymbol(company) {
export function getCurrencySymbol(currency) {
return currencySymbols?.data?.[currency]
}

export const currencyPrecision = createResource({
url: "frappe.client.get_single_value",
params: {
doctype: "System Settings",
field: "currency_precision"
},
auto: true,
initialData: 2
});
85 changes: 25 additions & 60 deletions frontend/src/views/employee_advance/Form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@
<script setup>
import { IonPage, IonContent } from "@ionic/vue"
import { createResource } from "frappe-ui"
import { ref, watch, inject, computed } from "vue"
import { ref, watch, inject } from "vue"

import FormView from "@/components/FormView.vue"

import { getCompanyCurrency } from "@/data/currencies"
import { updateCurrencyLabels } from "@/composables/useCurrencyConversion"

const employee = inject("$employee")

Expand All @@ -41,10 +40,6 @@ const employeeAdvance = ref({
department: employee.data.department,
})

const companyCurrency = computed(() =>
getCompanyCurrency(employeeAdvance.value.company)
)

// get form fields
const formFields = createResource({
url: "hrms.api.get_doctype_fields",
Expand All @@ -53,41 +48,31 @@ const formFields = createResource({
const fields = getFilteredFields(data)
return applyFilters(fields)
},
onSuccess(_) {
employeeCurrency.reload()
advanceAccount.reload()
},
})
formFields.reload()

const employeeCurrency = createResource({
url: "hrms.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency",
params: { employee: employee.data.name },
onSuccess(data) {
employeeAdvance.value.currency = data
setExchangeRate()
},
})

const exchangeRate = createResource({
url: "erpnext.setup.utils.get_exchange_rate",
onSuccess(data) {
employeeAdvance.value.exchange_rate = data
},
})

const advanceAccount = createResource({
url: "hrms.api.get_advance_account",
params: { company: employeeAdvance.value.company },
onSuccess(data) {
employeeAdvance.value.advance_account = data
// scripts
watch(
() => [formFields.data, employeeAdvance.value.currency],
([fields, currency]) => {
if (!fields || !currency) return

updateCurrencyLabels({
formFields: fields,
doc: employeeAdvance.value,
baseFields: ["base_paid_amount"],
transactionFields: ["paid_amount"],
})
},
})
{ immediate: true }
)

// form scripts
watch(
() => employeeAdvance.value.currency,
() => setExchangeRate()
(currency) => {
if (!currency) return
formFields.reload()
}
)

// helper functions
Expand All @@ -112,40 +97,20 @@ function getFilteredFields(fields) {
function applyFilters(fields) {
return fields.map((field) => {
if (field.fieldname === "advance_account") {
let currencies = [employeeAdvance.value.currency]
if (employeeAdvance.value.currency != companyCurrency.value)
currencies.push(companyCurrency.value)

if (!employeeAdvance.value.currency) return field

field.linkFilters = {
company: employeeAdvance.value.company,
is_group: 0,
root_type: "Asset",
is_group: 0,
account_type: "Receivable",
account_currency: ["in", currencies],
account_currency: ["in", [employeeAdvance.value.currency]],
company: employeeAdvance.value.company,
}
}

return field
})
}

function setExchangeRate() {
if (!employeeAdvance.value.currency) return
const exchange_rate_field = formFields.data?.find(
(field) => field.fieldname === "exchange_rate"
)

if (employeeAdvance.value.currency === companyCurrency.value) {
employeeAdvance.value.exchange_rate = 1
exchange_rate_field.hidden = 1
} else {
exchangeRate.fetch({
from_currency: employeeAdvance.value.currency,
to_currency: companyCurrency.value,
})
exchange_rate_field.hidden = 0
}
}

function validateForm() {}
</script>
Loading
Loading