summary history files

desktop/frontend/src/views/GetTransaction.vue
<script>
import { GetTransaction, UpdateTransaction, DeleteTransaction, UndeleteTransaction } from 'wailsjs/go/services/transactionService.js'
import { GetAccounts } from 'wailsjs/go/services/accountService.js'
import { GetTransactionNote, CreateTransactionNote, DeleteTransactionNote, UpdateTransactionNote } from 'wailsjs/go/services/noteService.js'
import { UpdateSplit, CreateSplit, DeleteSplit, UndeleteSplit } from 'wailsjs/go/services/splitsService.js'
import { CreateAttachment, GetTransactionAttachments, GetAttachment } from 'wailsjs/go/services/attachmentService.js'

export default {
  props: ['id'],
  data() {
    return {
      transaction: {
        attachments: [],
      },
      splits: [],
      accounts: [],
      note: {
        id: 0,
        note: "",
      },
    }
  },
  created() {
    this.$watch(
      () => this.$route.params,
      () => {
        this.getAccounts();
        this.getTransaction();
      },
      { immediate: true },
    )
  },
  beforeRouteUpdate: function(to, from, next) {
    this.getTransaction();
    next();
  },
  methods: {
    invertSign(value) {
      return -value;
    },

    async getAccounts() {
      let accounts = [];
      const { success, msg, data } = await GetAccounts()
      if (!success) {
          $message.error(msg)
          return
      }
      for (const account of data) {
        accounts.push({
          id: account.id,
          name: account.name,
          description: account.description,
          account_type: {
            id: account.account_type.id,
            name: account.account_type.name,
          },
        })
      }
      this.accounts = accounts
    },

    async getTransaction() {
      let transaction = {}
      let splits = []
      const { success, msg, data } = await GetTransaction(Number(this.id))

      if (!success) {
        $message.error(msg)
        return
      }

      transaction = {
        id: data.id,
        memo: data.memo,
        date: data.date,
        deleted: data.deleted,
        attachments: [],
      }

      for (const split of data.splits) {
        let s = {
          id: split.id,
          amount: split.amount,
          account: { ...split.account },
        }

        // Use account_id to keep track of account.id. Im unable to figure
        // out how to mutate nested account object.
        s.account_id = 0
        if (s.account && s.account.id) {
          s.account_id = s.account.id
        }

        splits.push(s)
      }

      if (splits.length === 1) {
        let s = {
          id: 0,
          amount: this.invertSign(splits[0].amount),
          account: 0,
        }
        splits.push(s)
      }

      const transactionNoteResp = await GetTransactionNote(Number(this.id))
      if (!transactionNoteResp.success) {
        $message.error(transactionNoteResp.msg)
        return
      }
      if ('note' in transactionNoteResp.data) {
        this.note = transactionNoteResp.data
      }

      const transactionAttachmentsResp = await GetTransactionAttachments(Number(this.id))
      if (!transactionAttachmentsResp.success) {
        $message.error(transactionAttachmentsResp.msg)
        return
      }
      let attachments = []
      for (const attachment of transactionAttachmentsResp.data) {
        attachments.push({
          id: attachment.id,
          name: attachment.name,
          size: attachment.size,
          data: attachment.data,
          createdAt: attachment.created_at,
        })
      }
      transaction.attachments = attachments

      this.transaction = transaction
      this.splits = splits
    },

    async updateTransaction() {
        const { success, msg, data } = await UpdateTransaction(Number(this.transaction.id), this.transaction.memo, this.transaction.date)
        if (!success) {
            $message.error(msg)
            return
        }
        let updateTransactionMessage = $message

        for (const split of this.splits) {
            if (typeof split.account_id === "undefined") {
              continue
            }

            if (split.id === 0) {
              const { success, msg, data } = await CreateSplit(this.transaction.id, split.account_id, split.amount)
              if (!success) {
                $message.error(msg)
                return
              }
              continue
            }

            const { success, msg, data } = await UpdateSplit(split.id, split.account_id, Number(split.amount))
            console.log("DEBUG1: " + JSON.stringify(split))
            if (!success) {
              $message.error(msg)
              return
            }
        }

        if (this.note.note.length != 0) {
          const newTransactionNoteResp = await CreateTransactionNote(this.transaction.id, this.note.note)
          if (!newTransactionNoteResp.success) {
            $message.error(newTransactionNoteResp.msg)
            return
          }
        }

        if (this.note.note.length == 0 && this.note.id != 0) {
          const deleteTransactionNoteResp = await DeleteTransactionNote(this.note.id)
          if (!deleteTransactionNoteResp.success) {
            $message.error(deleteTransactionNoteResp.msg)
            return
          }
        }

        if (this.note.id != 0) {
          const updateTransactionNoteResp = await UpdateTransactionNote(this.transaction.id, this.note.note)
          if (!updateTransactionNoteResp.success) {
            $message.error(updateTransactionNoteResp.msg)
            return
          }
        }

        updateTransactionMessage.success(msg)
        this.getTransaction()
        return
    },

    async deleteTransaction() {
        const { success, msg } = await DeleteTransaction(Number(this.transaction.id))
        if (!success) {
            $message.error(msg)
            return
        }

        const result = await this.deleteSplits()
        if (result instanceof Error) {
            $message.error(result.msg)
            return
        }

        $message.success(msg)
        this.getTransaction()
        return
    },

    async undeleteTransaction() {
        const { success, msg } = await UndeleteTransaction(Number(this.transaction.id))
        if (!success) {
            $message.error(msg)
            return
        }

        const result = await this.undeleteSplits()
        if (result instanceof Error) {
            $message.error(result.msg)
            return
        }

        $message.success(msg)
        this.getTransaction()
        return
    },

    async deleteSplits() {
        for (const split of this.splits) {
            const { success, msg } = await DeleteSplit(Number(split.id))
            if (!success) {
                return new Error(msg)
            }
        }
    },

    async undeleteSplits() {
        for (const split of this.splits) {
            const { success, msg } = await UndeleteSplit(Number(split.id))
            if (!success) {
                return new Error(msg)
            }
        }
    },

    newAttachment(event) {
      const files = event.target.files
      this.fileName = files[0].name
      const fileReader = new FileReader()
      fileReader.addEventListener('load', () => {
          this.fileContents = fileReader.result
          const createAttachmentResp = CreateAttachment(Number(this.transaction.id), this.fileName, this.fileContents)
          if (!createAttachmentResp.success) {
                $message.error(createAttachmentResp.msg)
                return
          }
      })
      fileReader.readAsDataURL(files[0])
    },

    async downloadAttachment(id) {
      const getAttachmentResp = await GetAttachment(Number(id))
      if (!getAttachmentResp.success) {
        $message.error(getAttachmentResp.msg)
        return
      }
    }
  },
}
</script>

<template>
    <!-- Page Wrapper -->
    <div id="wrapper">
      <Sidebar/>
      <slot/>

        <!-- Content Wrapper -->
        <div id="content-wrapper" class="d-flex flex-column">

            <!-- Main Content -->
            <div id="content">

                <Topbar/>
                <slot/>


                <!-- Begin Page Content -->
                <div class="container-fluid">

                    <div class="card shadow mb-4">
                        <div class="card-header py-3">
                            <h6 class="m-0 font-weight-bold text-primary">Transaction</h6>
                        </div>
                        <div class="card-body">
                            <form v-on:submit.prevent="updateTransaction">
                                <div class="mb-3">
                                    <label class="small mb-1">
                                      Date
                                    </label>
                                    <input class="form-control" type="text" v-model="transaction.date">
                                </div>
                                <div class="mb-3">
                                    <label class="small mb-1">
                                      Memo
                                    </label>
                                    <input class="form-control" type="text" v-model="transaction.memo">
                                </div>
                                <div v-for="(split, index) in splits" class="row gx-3 mb-3">
                                    <div class="col-md-6">
                                        <label class="small mb-1">Account</label>
                                        <div class="mb-3">
                                            <select class="form-select" aria-label="Type" v-model="splits[index].account_id">
                                                <option v-for="account in accounts" :key="account.id" :value="account.id">{{ account.name }}</option>
                                            </select>
                                        </div>
                                    </div>
                                    <div class="col-md-6">
                                        <label class="small mb-1">Amount</label>
                                        <input class="form-control" v-model="splits[index].amount" name="amount" type="number" value="0.00" step="any">
                                    </div>
                                </div>
                                <div class="mb-3">
                                    <label class="small mb-1">
                                      Attachments
                                    </label>
                                    <div class="mb-3">
                                      <input type="file" ref="fileInput" @change="newAttachment">
                                    </div>
                                    <div v-for="(attachment, index) in transaction.attachments" class="row gx-3 mb-3">
                                        <div class="col-md-6">
                                          <label class="small mb-1">
                                            <a @click.prevent="downloadAttachment(attachment.id)" v-text="attachment.name"/>
                                          </label>
                                        </div>
                                    </div>
                                </div>
                                <div class="mb-3">
                                  <label class="small mb-1">Notes</label>
                                  <div class="mb-3">
                                    <textarea class="form-control" type="text" rows=2 v-model="note.note"></textarea>
                                  </div>
                                </div>
                                <div class="row">
                                    <div class="col-auto">
                                        <button class="btn btn-danger btn-primary me-2" type="button" @click="undeleteTransaction" v-if="transaction.deleted">Undelete</button>
                                        <button class="btn btn-danger btn-primary me-2" type="button" @click="deleteTransaction" v-else>Delete</button>
                                    </div>
                                    <div class="col-auto">
                                        <button class="btn btn-primary" type="submit" v-if="transaction.deleted == false">Save changes</button>
                                    </div>
                                </div>
                            </form>
                        </div>
                    </div>
                </div>
                <!-- /.container-fluid -->

            </div>
            <!-- End of Main Content -->

            <!-- Footer -->
            <footer class="sticky-footer bg-white">
            </footer>
            <!-- End of Footer -->

        </div>
        <!-- End of Content Wrapper -->

    </div>
    <!-- End of Page Wrapper -->
</template>