340 lines
9.7 KiB
Vue
340 lines
9.7 KiB
Vue
<template>
|
|
<div v-if="transaction">
|
|
<nav class="breadcrumb" aria-label="breadcrumbs" v-if="account">
|
|
<ul>
|
|
<li>
|
|
<router-link
|
|
:to="{
|
|
name: 'account',
|
|
params: { id: account._id },
|
|
hash: `#${transaction._id}`
|
|
}"
|
|
>{{ account.name }}</router-link
|
|
>
|
|
</li>
|
|
<li
|
|
class="is-active"
|
|
v-if="transaction.tag && transaction.tag !== noTag"
|
|
>
|
|
<a href="#" @click.prevent>{{
|
|
transactionTagLabel[(transaction.tag || '').toLowerCase()].label
|
|
}}</a>
|
|
</li>
|
|
<li class="is-active">
|
|
<a href="#" @click.prevent>{{ transaction.name }}</a>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
<div class="buttons is-centered" v-if="account && !account.archive">
|
|
<router-link
|
|
v-if="canModify"
|
|
tag="button"
|
|
class="button"
|
|
:to="{ name: 'transaction-update', params: { id } }"
|
|
>
|
|
modifier
|
|
</router-link>
|
|
<confirm-button class="is-danger" @confirm="removeTransaction">
|
|
supprimer
|
|
</confirm-button>
|
|
</div>
|
|
<div class="columns no-margin is-centered">
|
|
<div class="column is-half">
|
|
<h3 class="subtitle is-3">{{ transaction.date | fulldate }}</h3>
|
|
<div class="resume">
|
|
Dépense payée par
|
|
<span class="pay-by">{{ transaction.payBy }}</span>
|
|
<span v-if="!uniqueUser">
|
|
pour
|
|
<span class="pay-for">{{ payForLabel.join(', ') }}</span>
|
|
</span>
|
|
<span v-if="transaction.location">
|
|
à {{ transaction.location.place }}
|
|
</span>
|
|
d'une somme de
|
|
<span class="numeric">{{
|
|
transaction.amount | money(transaction.mainCurrency)
|
|
}}</span>
|
|
.
|
|
</div>
|
|
</div>
|
|
<div v-if="transaction.exchange">
|
|
<div v-if="currencies.length" class="exchange column is-half">
|
|
<h3 class="subtitle is-3">
|
|
Taux d'échange
|
|
</h3>
|
|
<div class="columns is-centered">
|
|
<div class="column">
|
|
<table class="table is-fullwidth is-striped is-bordered">
|
|
<caption>
|
|
<span class="numeric">{{
|
|
transaction.amount | money(transaction.mainCurrency)
|
|
}}</span>
|
|
</caption>
|
|
<tbody>
|
|
<tr v-for="(currency, k) in currencies" :key="k">
|
|
<td class="numeric">
|
|
{{
|
|
(transaction && transaction.amount) ||
|
|
(1 * getRate(currency.code)) | moneypad(currency)
|
|
}}
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="column">
|
|
<table class="table is-fullwidth is-striped is-bordered">
|
|
<caption>
|
|
<span class="numeric">{{
|
|
1 | money(transaction.mainCurrency)
|
|
}}</span>
|
|
</caption>
|
|
<tbody>
|
|
<tr v-for="(currency, k) in currencies" :key="k">
|
|
<td class="numeric">
|
|
{{ getRate(currency.code) | moneypad(currency) }}
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="column">
|
|
<table
|
|
class="table is-hoverable is-fullwidth is-striped is-bordered"
|
|
>
|
|
<caption>
|
|
{{
|
|
$tc('transaction.money', currencies.length)
|
|
}}
|
|
</caption>
|
|
<tbody>
|
|
<tr v-for="(currency, k) in currencies" :key="k">
|
|
<td>{{ 1 | money(currency) }}</td>
|
|
<td class="numeric">
|
|
{{
|
|
(1 / getRate(currency.code))
|
|
| moneypad(transaction && transaction.mainCurrency)
|
|
}}
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-else class="column is-half">
|
|
<online-view @online="retrieveExchange" />
|
|
Le taux d'échange n'a pas pu être récupéré.
|
|
</div>
|
|
</div>
|
|
<earth-map v-if="transaction.location" :locations="transaction.location" />
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { Component, Prop, Vue } from 'vue-property-decorator'
|
|
import { Getter } from 'vuex-class'
|
|
import TransactionType from '@/enums/TransactionType'
|
|
import bus, { SYNC } from '@/utils/bus-event'
|
|
import notif from '@/utils/notif'
|
|
import TransactionTag, { TransactionTagLabel } from '@/enums/TransactionTag'
|
|
import accountService from '@/services/AccountService'
|
|
import exchangeService from '@/services/ExchangeService'
|
|
import transactionService from '@/services/TransactionService'
|
|
import IAccount from '@/models/IAccount'
|
|
import ICurrency from '@/models/ICurrency'
|
|
import ITransaction from '@/models/ITransaction'
|
|
import IUser from '@/models/IUser'
|
|
import ISplit from '@/models/ISplit'
|
|
|
|
@Component({
|
|
components: {
|
|
'online-view': () => import('@/components/OnlineView.vue'),
|
|
'earth-map': () => import('@/components/EarthMap.vue'),
|
|
'confirm-button': () => import('@/components/ConfirmButton.vue')
|
|
}
|
|
})
|
|
export default class TransactionItem extends Vue {
|
|
@Getter
|
|
public user!: IUser | null
|
|
@Prop({ type: String, required: true })
|
|
public id!: string
|
|
public transaction: ITransaction | null = null
|
|
public account: IAccount | null = null
|
|
public noTag: string = TransactionTag.None
|
|
public transactionTagLabel: typeof TransactionTagLabel = TransactionTagLabel
|
|
public removing: boolean = false
|
|
public earth: any | null = null
|
|
|
|
public created(): void {
|
|
this.getData()
|
|
bus.$on(SYNC, this.getData)
|
|
}
|
|
|
|
public beforeDestroy(): void {
|
|
bus.$off(SYNC, this.getData)
|
|
}
|
|
|
|
public async getData(docIds?: string[]): Promise<void> {
|
|
if (docIds && !docIds.find((i: string) => i === this.id)) {
|
|
return
|
|
}
|
|
try {
|
|
if (this.removing) {
|
|
return
|
|
}
|
|
this.transaction = await transactionService.get(this.id)
|
|
if (this.transaction) {
|
|
this.account = await accountService.get(this.accountId)
|
|
} else {
|
|
if (this.accountId) {
|
|
this.$router.push({ name: 'account', params: { id: this.accountId } })
|
|
notif.error('Transaction supprimée.')
|
|
} else {
|
|
this.$router.push({ name: 'home' })
|
|
notif.error('Compte inexistant.')
|
|
}
|
|
}
|
|
} catch (error) {
|
|
// tslint:disable-next-line
|
|
console.warn({ error })
|
|
this.$router.push({ name: 'home' })
|
|
}
|
|
}
|
|
|
|
public async removeTransaction(): Promise<void> {
|
|
if (!this.transaction) {
|
|
return
|
|
}
|
|
this.removing = true
|
|
const ok: boolean = await transactionService.remove(this.id)
|
|
if (ok) {
|
|
if (this.accountId) {
|
|
this.$router.push({
|
|
name: 'account',
|
|
params: {
|
|
id: this.accountId
|
|
}
|
|
})
|
|
} else {
|
|
this.$router.push({ name: 'home' })
|
|
}
|
|
}
|
|
this.removing = false
|
|
}
|
|
|
|
public async retrieveExchange(): Promise<void> {
|
|
if (this.transaction && !this.transaction.exchange) {
|
|
this.transaction.exchange = await exchangeService.get(
|
|
this.transaction.mainCurrency.code,
|
|
this.transaction.date
|
|
)
|
|
await transactionService.save(this.transaction)
|
|
}
|
|
}
|
|
|
|
public getRate(code: string) {
|
|
return (
|
|
(this.transaction &&
|
|
this.transaction.exchange &&
|
|
this.transaction.exchange.rates[code]) ||
|
|
1
|
|
)
|
|
}
|
|
|
|
public uniqueUser() {
|
|
if (!this.transaction) {
|
|
return false
|
|
}
|
|
return this.transaction.payBy === this.payForLabel.join(',')
|
|
}
|
|
|
|
public get accountId(): string {
|
|
return (this.transaction && this.transaction.accountId) || ''
|
|
}
|
|
|
|
public get canModify(): boolean {
|
|
if (!this.transaction) {
|
|
return false
|
|
}
|
|
return (
|
|
(this.transaction.transactionType === TransactionType.normal &&
|
|
this.isAdmin) ||
|
|
this.transaction.payBy === this.userAlias
|
|
)
|
|
}
|
|
|
|
public get userAlias(): string | undefined {
|
|
if (!this.account || !this.user) {
|
|
return ''
|
|
}
|
|
const user = this.user
|
|
return this.account.users
|
|
.map((u: IUser) => u.slugEmail)
|
|
.find((email: string | undefined) => email === user.slugEmail)
|
|
}
|
|
|
|
public get isAdmin(): boolean {
|
|
if (!this.account || !this.user) {
|
|
return true
|
|
}
|
|
return (
|
|
!this.account.admin ||
|
|
this.account.admin.slugEmail === this.user.slugEmail
|
|
)
|
|
}
|
|
|
|
public get payForLabel(): string[] {
|
|
if (!this.transaction) {
|
|
return []
|
|
}
|
|
return this.transaction.payFor.map((p: ISplit) => {
|
|
const part =
|
|
p.weight > 1
|
|
? `(${p.weight} ${this.$tc('transaction.weight', p.weight)})`
|
|
: ''
|
|
return `${p.alias} ${part}`.trim()
|
|
})
|
|
}
|
|
|
|
public get currencies(): ICurrency[] {
|
|
if (!this.transaction || !this.transaction.exchange) {
|
|
return []
|
|
}
|
|
const transaction = this.transaction
|
|
const exchange = transaction.exchange
|
|
return transaction.currencies.filter(
|
|
(currency: ICurrency) => exchange && currency.code !== exchange.base
|
|
)
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import url('https://api.mapbox.com/mapbox-gl-js/v1.8.0/mapbox-gl.css');
|
|
|
|
p.resume {
|
|
font-size: 18pt;
|
|
margin: 15px 0;
|
|
}
|
|
table.table {
|
|
margin: 15px auto 0;
|
|
max-width: 350px;
|
|
}
|
|
.main-cell {
|
|
vertical-align: middle;
|
|
}
|
|
.buttons {
|
|
margin-top: 15px;
|
|
}
|
|
.pay-by,
|
|
.pay-for {
|
|
font-weight: bold;
|
|
}
|
|
.numeric {
|
|
text-align: right;
|
|
}
|
|
</style>
|