Advertisement
Guest User

Untitled

a guest
Feb 23rd, 2022
114
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.31 KB | None | 0 0
  1. <template>
  2. <div>
  3. <ValidationObserver ref="form" v-slot="{ handleSubmit }">
  4. <form @submit.prevent="handleSubmit(onSubmit)">
  5. <div class="row mb-3">
  6. <div class="col-12 mb-4">
  7. <div class="d-flex justify-content-between align-items-center">
  8. <div class="page-inner-header">
  9. <h5><i class="bi bi-file-ruled mr-2"></i>
  10. New invoice</h5>
  11. <p class="text-muted mb-0 small">Create invoice</p>
  12. </div>
  13. <div class="row">
  14. <div class="col text-center">
  15. <img src="/assets/img/crm/invoice.svg" width="120px" alt="invoice">
  16. </div>
  17. </div>
  18. </div>
  19. </div>
  20. </div>
  21. <div class="row">
  22. <div class="col-md-6">
  23. <div><label for="invoice_date" class="form-control-label">Search</label></div>
  24.  
  25. <div class="row">
  26.  
  27.  
  28. <div class="col-md-3">
  29. <b-button v-b-modal.newCustomer class="btn-sm mb-3 mb-md-3">Add new Customer</b-button>
  30. </div>
  31. <div class="col-md-9">
  32.  
  33. <ValidationProvider vid="crm_customer_id" name="Customer" rules="required" v-slot="{ errors }">
  34. <typehead-component :url="customersApi" :initialize="customer"
  35. @input="onCustomer" :error="errors[0]"/>
  36.  
  37. </ValidationProvider>
  38. </div>
  39. </div>
  40. <div v-if="customer.id">
  41.  
  42. <div class="card p-4">
  43. <label for="invoice_date" class="form-control-label">Customer details:</label>
  44. <hr class="m-0">
  45. <div class="mt-3"><i class="bi bi-person mr-2"></i>{{ customer.name }}</div>
  46. <div v-if="customer.email"><i class="bi bi-inbox mr-2"></i> {{ customer.email }}</div>
  47. <div v-if="customer.address"><i class="bi bi-geo mr-2"></i>{{ customer.address }}</div>
  48. <div v-if="customer.phone_number"><i class="bi bi-phone mr-2"></i>{{ customer.phone_number }}</div>
  49. </div>
  50. </div>
  51. </div>
  52. <div class="col-md-6">
  53.  
  54. <div class="row">
  55. <div class="col-md-6">
  56. <ValidationProvider vid="invoice_date" name="Invoice Date" rules="required" v-slot="{ errors }">
  57. <label for="invoice_date" class="form-control-label">Invoice Date</label>
  58. <input type="date" v-model="invoice_date" id="invoice_date"
  59. class="form-control form-control-sm" :class="{'is-invalid': errors[0] }">
  60. <span v-if="errors[0]" class="invalid-feedback">{{ errors[0] }}</span>
  61. </ValidationProvider>
  62. </div>
  63. <div class="col-md-6">
  64. <ValidationProvider vid="due_date" name="Due Date" rules="required" v-slot="{ errors }">
  65. <label for="due_date" class="form-control-label">Due Date</label>
  66. <input type="date" v-model="due_date" id="due_date"
  67. class="form-control form-control-sm" :class="{'is-invalid': errors[0] }">
  68. <span v-if="errors[0]" class="invalid-feedback">{{ errors[0] }}</span>
  69. </ValidationProvider>
  70. </div>
  71. </div>
  72. </div>
  73. </div>
  74. <hr>
  75. <div class="row">
  76. <div class="col-12">
  77. <div class="invoice-scroll">
  78. <table class="table">
  79. <thead>
  80. <tr>
  81. <th class="col-1">No</th>
  82. <th class="col-4">Item</th>
  83. <th class="col-2">Price/unit</th>
  84. <th class="col-2">Quantity</th>
  85. <th class="col-2 text-right">Total</th>
  86. <th class="col-1"></th>
  87. </tr>
  88. </thead>
  89.  
  90. <tr v-for="(item, index) in items">
  91. <td data-label="No" class="p-4">{{ index + 1 }}</td>
  92. <td data-label="Item">
  93. <ValidationProvider name="description" rules="required|max:250|is_not:Item name"
  94. v-slot="{ errors }">
  95. <input type="text" v-model="item.description" class="form-control form-control-sm"
  96. :class="{'is-invalid': errors[0] }">
  97. <span v-if="errors[0]" class="invalid-feedback">{{ errors[0] }}</span>
  98. </ValidationProvider>
  99. </td>
  100. <td data-label="Price/unite">
  101. <ValidationProvider name="Price" rules="required|min_value:0.01" v-slot="{ errors }">
  102.  
  103. <input type="number" min="0" step="0.01" v-model="item.price"
  104. class="form-control form-control-sm" :class="{'is-invalid': errors[0] }">
  105. <span v-if="errors[0]" class="invalid-feedback">{{ errors[0] }}</span>
  106. </ValidationProvider>
  107. </td>
  108. <td data-label="Quantity">
  109. <ValidationProvider name="Quantity" rules="required|min_value:1" v-slot="{ errors }">
  110. <input type="number" min="0" v-model="item.quantity" class="form-control form-control-sm"
  111. :class="{'is-invalid': errors[0] }">
  112. <span v-if="errors[0]" class="invalid-feedback">{{ errors[0] }}</span>
  113. </ValidationProvider>
  114. </td>
  115. <td data-label="Total" class="text-right p-4"><strong>{{
  116. decimalDigits(item.price * item.quantity)
  117. }}</strong></td>
  118. <td class="text-right">
  119. <button class="btn btn-danger btn-sm"
  120. @click.prevent="deleteItem(index)"><i class="bi bi-x"></i>
  121. </button>
  122. </td>
  123. </tr>
  124.  
  125. </table>
  126. </div>
  127. </div>
  128. </div>
  129. <div class="row">
  130. <div class="col-12">
  131. <button @click.prevent="addNewItem" class="btn btn-success btn-sm">Add item</button>
  132. </div>
  133. </div>
  134. <hr>
  135. <div class="row">
  136. <div class="col-12 col-md-6 order-12 order-md-1">
  137. <div class="row">
  138. <div class="col-12">
  139. <p class="mb-n1"><label class="form-control-label">Notes:</label></p>
  140. <ValidationProvider vid="note" name="Notes" rules="max:1000" v-slot="{ errors }">
  141. <textarea class="form-control form-control-sm" :class="{'is-invalid': errors[0] }" rows="4"
  142. v-model="note"></textarea>
  143. <span v-if="errors[0]" class="invalid-feedback">{{ errors[0] }}</span>
  144. </ValidationProvider>
  145. </div>
  146. </div>
  147. <div class="row">
  148. <div class="col-12">
  149. <p class="mb-n1 mt-3"><label class="form-control-label">Terms:</label></p>
  150. <ValidationProvider vid="terms" name="Terms" rules="max:1000" v-slot="{ errors }">
  151. <textarea class="form-control form-control-sm" :class="{'is-invalid': errors[0] }" rows="4"
  152. v-model="terms"></textarea>
  153. <span v-if="errors[0]" class="invalid-feedback">{{ errors[0] }}</span>
  154. </ValidationProvider>
  155. </div>
  156. </div>
  157. </div>
  158. <div class="col-12 col-md-6 order-1 order-md-12">
  159. <div class="row">
  160. <div class="col-4"><label class="form-control-label">Subtotal:</label></div>
  161. <div class="col-4"></div>
  162. <div class="col-4 text-right">{{ decimalDigits(subTotal) }}</div>
  163. </div>
  164. <div class="row mt-2">
  165. <div class="col-12 col-md-4"><label class="form-control-label">Discount:</label></div>
  166. <div class="col-8 col-md-4">
  167. <div class="input-group input-group-sm">
  168. <input type="number" min="0" max="100" v-model="discountRate"
  169. class="form-control form-control-sm"/>
  170. <div class="input-group-append">
  171. <span class="input-group-text">%</span>
  172. </div>
  173. </div>
  174. </div>
  175. <div class="col-4 text-right">{{ decimalDigits(discountTotal) }}</div>
  176. </div>
  177. <div class="row mt-2">
  178. <div class="col-12 col-md-4"><label class="form-control-label">Tax:</label></div>
  179. <div class="col-8 col-md-4">
  180. <div class="input-group input-group-sm">
  181. <input type="number" min="0" max="100" step="1" v-model="taxRate"
  182. class="form-control form-control-sm"/>
  183. <div class="input-group-append">
  184. <span class="input-group-text">%</span>
  185. </div>
  186. </div>
  187. </div>
  188. <div class="col-4 text-right">{{ decimalDigits(taxTotal) }}</div>
  189. </div>
  190. <div class="row mt-2">
  191. <div class="col-8"><label class="form-control-label">Total:</label></div>
  192. <div class="col-4 text-right">{{ decimalDigits(grandTotal) }}</div>
  193. </div>
  194. </div>
  195. </div>
  196. <div class="row">
  197. <div class="col-12 mt-5">
  198. <input type="submit" value="Create invoice" class="btn btn-primary btn-sm w-100" :disabled="loadingBtn">
  199. </div>
  200. </div>
  201. </form>
  202. <new-customer-component @customer="addNewCustomer"></new-customer-component>
  203. </ValidationObserver>
  204. </div>
  205. </template>
  206.  
  207. <script>
  208. import TypeheadComponent from "../../../helpers/Typehead.vue";
  209. import NewCustomerComponent from "./NewCustomerComponent";
  210.  
  211. export default {
  212. components: {
  213. TypeheadComponent,
  214. NewCustomerComponent
  215. },
  216. data() {
  217. return {
  218. loadingBtn: false,
  219. invoiceDate: '',
  220. items: [{
  221. description: 'Item name',
  222. quantity: 1,
  223. price: 0
  224. }],
  225. customers: [],
  226. customer: {
  227. id: '',
  228. name: '',
  229. email: '',
  230. address: '',
  231. phone_number: ''
  232. },
  233. invoiceCurrency: '£',
  234. discountRate: 0,
  235. taxRate: 20,
  236. due_date: '',
  237. invoice_date: '',
  238. terms: '',
  239. note: '',
  240. customersApi: '/crm/api/customer',
  241. crm_customer_id: ''
  242. }
  243. },
  244. computed: {
  245. subTotal() {
  246. return this.items.reduce(function (accumulator, item) {
  247. return accumulator + (item.price * item.quantity);
  248. }, 0);
  249. },
  250. discountTotal() {
  251. return this.subTotal * (this.discountRate / 100);
  252. },
  253. taxTotal() {
  254. return (this.subTotal - this.discountTotal) * (this.taxRate / 100);
  255. },
  256. grandTotal() {
  257. return (this.subTotal - this.discountTotal) + this.taxTotal;
  258. }
  259. },
  260. methods: {
  261. addNewCustomer(value) {
  262. this.customers.push(value);
  263. },
  264. resetFields() {
  265. let initialData = initialState();
  266. for (let prop in initialData) {
  267. this[prop] = initialData[prop];
  268. }
  269. },
  270. onCustomer(e) {
  271. const customer = e.target.value;
  272. this.customer.id = customer.id;
  273. this.customer.name = customer.name;
  274. this.customer.email = customer.email;
  275. this.customer.address = customer.address;
  276.  
  277. },
  278. onSubmit() {
  279. alert('hit');
  280. this.loadingBtn = true;
  281. axios.post('/crm/invoice', {
  282. due_date: this.due_date,
  283. invoice_date: this.invoice_date,
  284. crm_customer_id: this.customer.id,
  285. tax: this.taxRate,
  286. sub_total: this.subTotal,
  287. discount: this.discountTotal,
  288. discount_rate: this.discountRate,
  289. grand_total: this.grandTotal,
  290. terms: this.terms,
  291. note: this.note,
  292. items: this.items
  293. })
  294. .then((response) => {
  295. this.$notify({
  296. type: 'success',
  297. group: 'crm',
  298. title: 'Success',
  299. text: 'Your Invoice has been created'
  300. });
  301. setTimeout(() => {
  302. window.location.href = '/crm/invoice';
  303. }, 500)
  304. })
  305. .catch((error) => {
  306. this.loadingBtn = false;
  307. if (error.response.status === 422) {
  308. this.$notify({
  309. type: 'error',
  310. group: 'crm',
  311. title: 'Validation errors',
  312. text: error.response.data.message
  313. });
  314. this.$refs.form.setErrors(error.response.data.errors)
  315. } else {
  316. this.$notify({
  317. type: 'warn',
  318. group: 'crm',
  319. title: 'Unexpected error',
  320. text: 'Unexpected error, please try later.'
  321. });
  322. }
  323. });
  324. },
  325. addNewItem() {
  326. this.items.push({
  327. description: 'Item name',
  328. quantity: 0,
  329. price: 0
  330. })
  331. },
  332. deleteItem(index) {
  333. this.items.splice(index, 1)
  334. },
  335. decimalDigits(value) {
  336. return this.invoiceCurrency + ' ' + value.toFixed(2);
  337. },
  338. }
  339. };
  340.  
  341. </script>
  342.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement