Added primitive support to send bills to LHDN.
This commit is contained in:
parent
38d67f5f63
commit
822b7e908c
|
|
@ -12,5 +12,12 @@
|
|||
"COBOL",
|
||||
"ACUCOBOL"
|
||||
],
|
||||
"workbench.colorCustomizations": {
|
||||
"editorCursor.foreground": "#ef857d",
|
||||
"activityBarBadge.background": "#e9dbb7",
|
||||
"activityBarBadge.foreground": "#262626",
|
||||
"statusBar.background": "#9f5a51",
|
||||
"statusBar.foreground": "#e9dbb7"
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ import axios from 'axios'
|
|||
import router from '@/router'
|
||||
import store from './store/store';
|
||||
|
||||
const baseURL = 'https://api.ira-lex.com/'
|
||||
// const baseURL = 'https://api.ira-lex.com/'
|
||||
const baseURL = 'https://newback.ira-lex.com/'
|
||||
// const baseURL = 'http://192.168.2.125:8000/'
|
||||
// const baseURL = 'https://test.ira-lex.com/'
|
||||
// 'Cache-Control': 'no-cache',
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -148,6 +148,17 @@
|
|||
<a-menu-item key="6" @click="InvoiceDisplay(text.record,true)" v-if="hasPermission('billing.fund.html')"><feather-icon icon="MonitorIcon" svg-classes="text-warning w-4 h-4" class="pr-2"/>View</a-menu-item>
|
||||
<a-menu-item key="7" @click="goToRecordPaymentOrSendFund(text.record)" v-if="hasPermission('billing.payment') && approveNotDueDate(text.record)"><feather-icon icon="DollarSignIcon" svg-classes="text-primary w-4 h-4" class="pr-2"/>Record Payment</a-menu-item>
|
||||
<a-menu-item key="8" @click="goToRecordPaymentOrSendFund(text.record,'sendFund')" v-if="hasPermission('billing.fund-request.send') && text.record.status === 2"><feather-icon icon="MessageCircleIcon" svg-classes="text-warning w-4 h-4" class="pr-2"/>Send Payment Reminder</a-menu-item>
|
||||
|
||||
<!-- Only visible for bill/invoice (i.e. not fund request
|
||||
or official receipt), and when the bill is either unpaid or
|
||||
paid. -->
|
||||
<a-menu-item
|
||||
key="9"
|
||||
@click="sendToLhdn(text.record)"
|
||||
v-if="(hasPermission('billing.fund-request.send') || hasPermission('billing.fund-request.pdf')) && (text.record.status === 3 || text.record.status === 4) && (text.record.type === BILLING_TYPE_BILL)"
|
||||
>
|
||||
<feather-icon icon="MailIcon" svg-classes="text-secondary w-4 h-4" class="pr-2"/>Send to LHDN
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
<a-button style="margin-left: 8px" @click="actionStatusBill(text.record)"> {{ statusBill(text.record) }} <a-icon type="down" /> </a-button>
|
||||
</a-dropdown>
|
||||
|
|
@ -587,6 +598,19 @@
|
|||
</XTable>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
|
||||
<div id="billing-error-messages">
|
||||
<div v-if="lhdnErrors.length" class="mt-8">
|
||||
<a-alert
|
||||
v-for="(error, index) in lhdnErrors"
|
||||
:message="`${error.name}: ${error.msg}`"
|
||||
type="error"
|
||||
show-icon
|
||||
closable
|
||||
class="mb-4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</vx-card>
|
||||
|
||||
<deleteItem
|
||||
|
|
@ -1039,7 +1063,6 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import * as TableCol from "./billTbl";
|
||||
import * as TableColReceipt from "./receiptTbl";
|
||||
|
|
@ -1078,8 +1101,11 @@ export default {
|
|||
BillingViewModal,
|
||||
},
|
||||
|
||||
data(){
|
||||
return{
|
||||
data() {
|
||||
return {
|
||||
BILLING_TYPE_BILL : 1,
|
||||
BILLING_TYPE_FUND_REQUEST : 2,
|
||||
|
||||
model : null,
|
||||
modelReceipt : null,
|
||||
openQuickBill: false,
|
||||
|
|
@ -1106,12 +1132,12 @@ export default {
|
|||
tab_bill: '1',
|
||||
template_data: {},
|
||||
loading:false,
|
||||
loadingReceipt: false
|
||||
loadingReceipt: false,
|
||||
lhdnErrors: [],
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
|
||||
async handlerChangeReceipt(value) {
|
||||
methods: {
|
||||
async handlerChangeReceipt(value) {
|
||||
if (value) {
|
||||
try {
|
||||
this.loadingReceipt = true
|
||||
|
|
@ -1648,9 +1674,38 @@ export default {
|
|||
this.edit_mode = false;
|
||||
this.hasReceiptClient = false
|
||||
this.formReceipt.resetFields()
|
||||
}
|
||||
},
|
||||
|
||||
async sendToLhdn(record) {
|
||||
try {
|
||||
this.$vs.loading()
|
||||
const { data } = await axios.get(`billing/lhdn/${record.id}`)
|
||||
this.$notification['success']({
|
||||
message: 'Success',
|
||||
description: 'Your bill was submitted to LHDN. Check your LHDN panel to make sure there aren\'t any issues with the submitted invoice.',
|
||||
})
|
||||
} catch (e) {
|
||||
if (e.status === 422) {
|
||||
this.lhdnErrors = e.data.details
|
||||
this.$nextTick(() => {
|
||||
const errorsEl = document.getElementById('billing-error-messages')
|
||||
if (errorsEl) {
|
||||
errorsEl.scrollIntoView({ behavior: 'smooth', block: "start" })
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$notification['error']({
|
||||
message: "Error",
|
||||
description: "Failed to submit the document to LHDN. This feature is fairly new and might not work correctly. Please contact support to resolve your problem.",
|
||||
})
|
||||
// this.$message.error(`${err.name}: ${err.msg}`)
|
||||
}
|
||||
} finally {
|
||||
this.$vs.loading.close()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
currentDateFormat() {
|
||||
return moment().format('YYYY-MM-DD')
|
||||
|
|
@ -1659,10 +1714,12 @@ export default {
|
|||
return localStorage.getItem('currency_symbol')
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.model = TableCol.default
|
||||
this.modelReceipt = TableColReceipt.default
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.is_click_add_time = true
|
||||
if (this.$route.params.query) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<a-form :form="form" @submit.prevent="handleSubmit" autocomplete="off">
|
||||
|
||||
<!-- Contacts Card -->
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<div class="w-full md:w-1/6">
|
||||
<h4 class="text-gray-900 text-lg">Contacts</h4>
|
||||
|
|
@ -28,7 +30,9 @@
|
|||
</vs-card>
|
||||
</div>
|
||||
</div>
|
||||
<a-divider/>
|
||||
|
||||
<!-- Contact Information -->
|
||||
<a-divider />
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<div class="w-full md:w-1/6">
|
||||
<h4 class="text-gray-900 text-lg">Contact Information</h4>
|
||||
|
|
@ -280,9 +284,8 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<a-divider/>
|
||||
|
||||
<!-- ADDRESS SECTION -->
|
||||
<a-divider />
|
||||
<section class="flex flex-col md:flex-row scoll_bottom">
|
||||
<!-- TITLE -->
|
||||
<div class="w-full md:w-1/6">
|
||||
|
|
@ -379,6 +382,62 @@
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<!-- E-Invoicing Information -->
|
||||
<a-divider/>
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<div class="w-full md:w-1/6">
|
||||
<h4 class="text-gray-900 text-lg">E-Invoicing Information</h4>
|
||||
</div>
|
||||
<div class="w-full md:w-5/6">
|
||||
<vs-card class="p-5">
|
||||
<div class="grid sm:grid-cols-1 md:grid-cols-2 gap-x-4">
|
||||
<template v-if="radioType === CONTACT_TYPE_PERSON">
|
||||
<a-form-item label="BRN" has-feedback>
|
||||
<a-input
|
||||
v-decorator="['person.brn']"
|
||||
placeholder="Business Registration Number"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="SST Registration Number" has-feedback>
|
||||
<a-input
|
||||
v-decorator="['person.sst_registeration_number']"
|
||||
placeholder="SST Registration Number"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="TIN" has-feedback>
|
||||
<a-input
|
||||
v-decorator="['person.tin']"
|
||||
placeholder="Tax Identification Number"
|
||||
/>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<template v-else-if="radioType === CONTACT_TYPE_COMPANY">
|
||||
<a-form-item label="BRN" has-feedback>
|
||||
<a-input
|
||||
v-decorator="['company.brn']"
|
||||
placeholder="Business Registration Number"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="SST Registration Number" has-feedback>
|
||||
<a-input
|
||||
v-decorator="['company.sst_registeration_number']"
|
||||
placeholder="SST Registration Number"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="TIN" has-feedback>
|
||||
<a-input
|
||||
v-decorator="['company.tin']"
|
||||
placeholder="Tax Identification Number"
|
||||
/>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</vs-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
|
|
@ -399,13 +458,20 @@ import moment from 'moment'
|
|||
|
||||
export default {
|
||||
name: "ContactAdd",
|
||||
|
||||
mixins: [Mixin],
|
||||
components:{
|
||||
|
||||
components: {
|
||||
Xupload,
|
||||
Footer
|
||||
},
|
||||
data(){
|
||||
return{
|
||||
|
||||
data() {
|
||||
return {
|
||||
// CONSTANTS
|
||||
CONTACT_TYPE_PERSON: 1,
|
||||
CONTACT_TYPE_COMPANY: 2,
|
||||
|
||||
imageUrl: '',
|
||||
loading: false,
|
||||
cities:[],
|
||||
|
|
@ -425,8 +491,8 @@ export default {
|
|||
radioType: 1
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
|
||||
methods:{
|
||||
handleRadio(e){
|
||||
this.radioType = e.target.value
|
||||
},
|
||||
|
|
|
|||
|
|
@ -45,71 +45,74 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import axios from "@/axios"
|
||||
import Mixin from '@/views/mixin/mixin'
|
||||
export default {
|
||||
name: "SelectOption",
|
||||
mixins: [Mixin],
|
||||
components:{
|
||||
VNodes: {
|
||||
functional: true,
|
||||
render: (h, ctx) => ctx.props.vnodes,
|
||||
},
|
||||
},
|
||||
props:{
|
||||
url : { type : String,default:'/account/list/' },
|
||||
label : { type : String },
|
||||
field : { type : String },
|
||||
method : { type: String , default: 'post' },
|
||||
required : {type: Boolean , default: false},
|
||||
is_initial_value : {type: Boolean , default: false},
|
||||
mode : { type: String, default: 'default' },
|
||||
placeholder : { type: String , default:'notification' },
|
||||
is_add_extra_footer: { type: Boolean , default:false }
|
||||
},
|
||||
data(){
|
||||
return{
|
||||
data:[],
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
getUser(){
|
||||
return JSON.parse(localStorage.getItem('user'))
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
async getMatter(){
|
||||
try{
|
||||
this.loading = true
|
||||
if(this.method == 'post'){
|
||||
const { data } = await axios.post(this.url,{limit:100})
|
||||
this.data = data.rows
|
||||
}else{
|
||||
const { data } = await axios.get(this.url)
|
||||
this.data = data
|
||||
}
|
||||
}catch (e) {
|
||||
throw e
|
||||
}finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
handleSelect(value){
|
||||
this.$emit('SelectUser',value)
|
||||
},
|
||||
emitClicker(){
|
||||
this.$emit('emitClicker')
|
||||
},
|
||||
handlerChange(value) {
|
||||
this.$emit('change', value)
|
||||
}
|
||||
import axios from "@/axios"
|
||||
import Mixin from '@/views/mixin/mixin'
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.getMatter()
|
||||
},
|
||||
export default {
|
||||
name: "SelectOption",
|
||||
mixins: [Mixin],
|
||||
components:{
|
||||
VNodes: {
|
||||
functional: true,
|
||||
render: (h, ctx) => ctx.props.vnodes,
|
||||
},
|
||||
},
|
||||
props:{
|
||||
url : { type : String,default:'/account/list/' },
|
||||
label : { type : String },
|
||||
field : { type : String },
|
||||
method : { type: String , default: 'post' },
|
||||
required : {type: Boolean , default: false},
|
||||
is_initial_value : {type: Boolean , default: false},
|
||||
mode : { type: String, default: 'default' },
|
||||
placeholder : { type: String , default:'notification' },
|
||||
is_add_extra_footer: { type: Boolean , default:false }
|
||||
},
|
||||
data(){
|
||||
return{
|
||||
data:[],
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
getUser(){
|
||||
return JSON.parse(localStorage.getItem('user'))
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
async getMatter() {
|
||||
try {
|
||||
this.loading = true
|
||||
if (this.method == 'post') {
|
||||
const { data } = await axios.post(this.url, { limit: 100 })
|
||||
this.data = data.rows
|
||||
this.$emit('loaded', data.rows)
|
||||
} else {
|
||||
const { data } = await axios.get(this.url)
|
||||
this.data = data
|
||||
this.$emit('loaded', data)
|
||||
}
|
||||
} catch (e) {
|
||||
throw e
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
handleSelect(value){
|
||||
this.$emit('SelectUser',value)
|
||||
},
|
||||
emitClicker(){
|
||||
this.$emit('emitClicker')
|
||||
},
|
||||
handlerChange(value) {
|
||||
this.$emit('change', value)
|
||||
}
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.getMatter()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -37,18 +37,23 @@
|
|||
autocomplete="none"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="Registration Number">
|
||||
<a-form-item label="BRN">
|
||||
<a-input
|
||||
v-decorator="['brn']"
|
||||
autocomplete="none"
|
||||
placeholder="Business Registration Number"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="SST Registration Number">
|
||||
<a-input
|
||||
v-decorator="['sst_registeration_number']"
|
||||
autocomplete="none"
|
||||
placeholder="SST Registration Number"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="MSIC Code" help="Malaysia Standard Industrial Classification">
|
||||
<a-input
|
||||
v-decorator="['msic_code']"
|
||||
autocomplete="none"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@
|
|||
<template slot="footer">
|
||||
<div v-if="result.length" class="grid grid-cols-5">
|
||||
<div class="font-bold">Total</div>
|
||||
<div class="place-self-center mr-18">{{ sumCol(result,'complete') }}</div>
|
||||
<div class="place-self-center mr-20">{{ sumCol(result,'in_progress') }}</div>
|
||||
<div class="place-self-start ml-20">{{ sumCol(result,'overdue') }}</div>
|
||||
<div class="place-self-start ml-10">{{ sumCol(result,'upcoming') }}</div>
|
||||
<div class="place-self-center mr-18">{{ sumCol(result,'complete').slice(0, -3) }}</div>
|
||||
<div class="place-self-center mr-20">{{ sumCol(result,'in_progress').slice(0, -3) }}</div>
|
||||
<div class="place-self-start ml-20">{{ sumCol(result,'overdue').slice(0, -3) }}</div>
|
||||
<div class="place-self-start ml-10">{{ sumCol(result,'upcoming').slice(0, -3) }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
|
|
|
|||
Loading…
Reference in New Issue