diff --git a/.eslintrc b/.eslintrc index 695b75d3..e640df4f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -10,6 +10,7 @@ "@vue/standard" ], "rules": { - "vue/max-attributes-per-line": "off" + "vue/max-attributes-per-line": "off", + "vue/camelcase": "off" } } \ No newline at end of file diff --git a/.gitignore b/.gitignore index adb09eb6..8211efde 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,6 @@ yarn-error.log vendor/composer/autoload_static.php vendor/composer/autoload_classmap.php package-lock.json -composer.lock \ No newline at end of file +composer.lock +config/database.php +notes.txt diff --git a/DroidSansFallback.ttf b/DroidSansFallback.ttf new file mode 100644 index 00000000..1099b177 Binary files /dev/null and b/DroidSansFallback.ttf differ diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php new file mode 100644 index 00000000..acd4e542 --- /dev/null +++ b/app/Http/Controllers/InvoiceController.php @@ -0,0 +1,667 @@ +json(["message" => "Invalid id. Id needs to be a number"], 400); + } + + // only allow if belongs or is admin + if ($request->user()->role === 'member') { + if (!$this->isUser($id, $request->user()->id)) { + return response()->json(['message' => 'The Invoice Does not belongs to you.'], 403); + } + } + + $invoice = Invoice::where('booking_id', $id)->first(); + if (!$invoice) { + return response()->json(['message' => 'Invoice Not Found'], 404); + } + $lines = InvoiceDetails::where('invoice_id', $invoice['id'])->orderBy('order', 'asc')->get(); + + $status = InvoiceStatuses::where('invoice_id', $invoice['id'])->orderBy('created_at', 'asc')->get(); + + $invoice->lines = $lines; + $invoice->status = $status; + return response()->json($invoice, 200); + + } + + public function create(Request $request, $id) + { + $user = $request->user(); + + // validate $id + if (!is_numeric($id)) { + return response()->json(["message" => "Invalid id. Id needs to be a number"], 400); + } + + $validatedData = $request->validate([ + 'address' => 'required', + 'buyer_company' => 'required', + 'contact_no' => 'required', + 'marking' => 'required', + 'reg_no' => 'required', + 'lines.*.order' => 'required|numeric', + 'lines.*.stock_code' => 'required', + 'lines.*.description' => 'max:256', + 'lines.*.quantity' => 'required|numeric', + 'lines.*.unit_price_rm' => 'required|numeric', + 'lines.*.unit_price_rmb' => '', + 'lines.*.total' => 'required|numeric', + ]); + + // booking_id belongs to user + $booking = Booking::where('id', $id)->first(); + if (!$booking) { + return response()->json(['message' => 'Unable to find booking'], 403); + } + + // user_id in booking does not match + if ($booking->user_id !== $user->id) { + return response()->json([ + 'message' => 'Booking does not belongs to you.' + ], 403); + } + + // Amount match with total + $subtotalArray = array_map( + function ($line) { return $line['total']; }, + $validatedData['lines'] + ); + $total = array_reduce($subtotalArray, function ($v1, $v2) { + return $v1 + $v2; + }); + if ($total !== $booking->amount) { + return response()->json([ + 'message' => 'Booking amount expected is '.$booking->amount.'. Your Invoice amount is '.$total + ], 400); + } + + // Write to database if does not exist + $exist = Invoice::where('booking_id', $id)->first(); + if ($exist) { + return response() + ->json(['message' => 'Invoice exist. Cannot create duplicate invoice. Try edit.'], 400); + } + + // Generate PO Number - po20200810-001(PO Format) + $dt = Carbon::now(); + $po_number = 'po'.$dt->format('Ymmdd').'-'.$id; + + $invoice = new Invoice; + $invoice->amount = $total; + $invoice->booking_id = (int)$id; + $invoice->reg_no = $validatedData['reg_no']; + $invoice->buyer_company = $validatedData['buyer_company']; + $invoice->address = $validatedData['address']; + $invoice->contact_no = $validatedData['contact_no']; + $invoice->po_number = $po_number; + $invoice->save(); + + if (!$invoice) { + return response() + ->json(['message' => 'Unable to Save Data'], 500); + } + + // Insert invoice_id into lines + foreach( $validatedData['lines'] as $key => $line) { + $invoice_details = new InvoiceDetails; + $invoice_details->order = $line['order']; + $invoice_details->description = $line['description']; + $invoice_details->quantity = $line['quantity']; + $invoice_details->stock_code = $line['stock_code']; + $invoice_details->invoice_id = $invoice->id; + $invoice_details->unit_price_rmb = $line['unit_price_rmb']; + $invoice_details->unit_price_rm = $line['unit_price_rm']; + $invoice_details->total = $line['total']; + + $invoice_details->save(); + } + unset($line); + + $invoice->lines = $invoice_details; + + // Post status as pending + $status = new InvoiceStatuses; + $status->status = 'pending'; + $status->comment = ''; + $status->invoice_id = $invoice->id; + $status->save(); + + if($invoice_details) { + return $this->show($request, $id); + } else { + return response(500); + } + } + + public function edit(Request $request, $id) + { + $user = $request->user(); + + // validate $id + if (!is_numeric($id)) { + return response()->json(["message" => "Invalid id. Id needs to be a number"], 400); + } + + $validatedData = $request->validate([ + 'address' => 'required', + 'buyer_company' => 'required', + 'contact_no' => 'required', + 'marking' => 'required', + 'reg_no' => 'required', + 'lines.*.order' => 'required|numeric', + 'lines.*.stock_code' => 'required', + 'lines.*.description' => 'max:256', + 'lines.*.quantity' => 'required|numeric', + 'lines.*.unit_price_rm' => 'required|numeric', + 'lines.*.unit_price_rmb' => '', + 'lines.*.total' => 'required|numeric', + ]); + + // booking_id belongs to user + $booking = Booking::where('id', $id)->first(); + if (!$booking) { + return response()->json(['message' => 'Unable to find booking'], 403); + } + + $invoice = Invoice::where('booking_id', $id)->first(); + if (!$invoice) { + return response()->json(['message' => 'Invoice Not Found'], 404); + } + + // role === 'member' && $booking->user_id !== user()->id && $currentstatus !== 'request_change') + if ($request->user()->role === 'member') { + if ($booking->user_id !== $request->user()->id) { + return response()->json([ + 'message' => 'Booking does not belongs to you' + ], 403); + } + + $currentstatus = InvoiceStatuses::where('invoice_id', $invoice['id'])->latest('created_at')->first()->status; + if ($currentstatus !== 'request_change') { + return response()->json(['message' => 'Submitted PO can only be changed when status is request_change'], 403); + } + } + + // Amount match with total + $subtotalArray = array_map( + function ($line) { return $line['total']; }, + $validatedData['lines'] + ); + $total = array_reduce($subtotalArray, function ($v1, $v2) { + return $v1 + $v2; + }); + if ($total != $booking->amount) { + return response()->json([ + 'message' => 'Booking amount expected is '.$booking->amount.'. Your Invoice amount is '.$total + ], 400); + } + + // Generate PO Number - po20200810-001(PO Format) + $dt = Carbon::now(); + $po_number = 'po'.$dt->format('Ymd').'-'.$id; + + $invoice->amount = $total; + $invoice->booking_id = (int)$id; + $invoice->reg_no = $validatedData['reg_no']; + $invoice->buyer_company = $validatedData['buyer_company']; + $invoice->address = $validatedData['address']; + $invoice->contact_no = $validatedData['contact_no']; + $invoice->po_number = $po_number; + $invoice->save(); + + if (!$invoice) { + return response() + ->json(['message' => 'Unable to Save Data'], 500); + } + + // Remove all invoice_details corresponding to invoice_id + $invoice_details = InvoiceDetails::where('invoice_id', $invoice->id); + $invoice_details->delete(); + + // Insert invoice_id into lines + foreach( $validatedData['lines'] as $key => $line) { + $invoice_details = new InvoiceDetails; + $invoice_details->order = $line['order']; + $invoice_details->description = $line['description']; + $invoice_details->quantity = $line['quantity']; + $invoice_details->stock_code = $line['stock_code']; + $invoice_details->invoice_id = $invoice->id; + $invoice_details->unit_price_rmb = $line['unit_price_rmb']; + $invoice_details->unit_price_rm = $line['unit_price_rm']; + $invoice_details->total = $line['total']; + + $invoice_details->save(); + } + unset($line); + + // Post status as pending + $status = new InvoiceStatuses; + $status->status = 'pending'; + if ($request->user()->role === 'admin') { + $status->comment = 'Edited by Admin.'; + } else { + $status->comment = 'Edited by Member.'; + } + $status->invoice_id = $invoice->id; + $status->save(); + + if($invoice_details) { + return $this->show($request, $id); + } else { + return response(500); + } + } + + public function postStatus(Request $request, $id) + { + + // validate $id is integer + if (!is_numeric($id)) { + return response()->json(["message" => "Invalid id. Id needs to be a number"], 400); + } + + $validatedData = $request->validate([ + 'status' => ['required', Rule::in('request_change', 'approve')], + 'comment' => Rule::requiredIf($request->status === 'request_change') + ]); + + // Save status to database + $status = new InvoiceStatuses; + $status->status = $validatedData['status']; + $status->comment = $validatedData['comment']; + $status->invoice_id = Invoice::where('booking_id', $id)->first()['id']; + $status->save(); + + $booking = Booking::find($id); + + $monthlycount = Invoice::where('ei', 'like', 'EI-'.Carbon::createFromFormat('Y-m-d H:i:s', $booking->created_at)->format('Ym').'%')->count() + 1; + $generatednumber = Carbon::createFromFormat('Y-m-d H:i:s', $booking->created_at)->format('Ym').'-'.sprintf("%05d", $monthlycount); + + $invoice = Invoice::where('booking_id', $id)->first(); + $invoice->ei = 'EI-'.$generatednumber; + $invoice->edo = 'EDO-'.$generatednumber; + $invoice->save(); + + if(!$status) { + return response()->json(['message' => 'Unable to Update Status'], 500); + } + + // Change Status to 7 if approve + // Add email event here + if ($validatedData['status'] === 'approve') { + $booking = Booking::find($id); + $booking->status = 7; + $booking->admin_status = 7; + $booking->save(); + + if(!$booking) { + return response()->json(['message' => 'Unable to Complete Booking. Please Approve Again'], 500); + } + } + + // Response + return response()->json($status, 201); + + + } + + public function customerToCIEFPO(Request $request, $id) + { + if (!is_numeric($id)) { + return response()->json(["message" => "Invalid id. Id needs to be a number"], 400); + } + + // Booking Exist and Belongs to User or is Admin + if ($request->user()->role === 'member') { + if (!$this->isUser($id, $request->user()->id)) { + return response()->json(['message' => 'The PO Does not belongs to you.'], 403); + } + } + + // Ensure Invoice Status is Approve + // if ($this->invoiceStatus($id) !== 'approve') { + // return response()->json(['message' => 'Invoice must and exist and approve status.'], 403); + // } + + // Generate PO + + $booking = Booking::where('id', $id)->first(); + $invoice = Invoice::where('booking_id', $booking['id'])->first(); + $lines = InvoiceDetails::where('invoice_id', $invoice->id)->get(); + $marking = User::find($booking->user_id)->marking; + + $data = [ + 'title' => 'Purchase Order', + 'detail' => [ + 'date' => Carbon::createFromFormat('Y-m-d H:i:s', $booking->created_at)->format('d-m-Y'), + 'po' => $invoice->po_number, + 'do' => '', + 'ref' => $booking->id + ], + 'buyer' => [ + 'buyer_company' => $invoice->buyer_company, + 'reg_no' => $invoice->reg_no, + 'address' => $invoice->address, + 'gst' => '', + 'phone' => $invoice->contact_no, + 'marking_no' => $marking + ], + 'seller' => [ + 'seller_company' => 'CIEF Worldwide Sdn Bhd (1134596-M)', + 'address' => 'Malaysia Global Innovation & Creativity Centre, Level + 1 CWS, Block 3730, Persiaran APEC 63000 + Cyberjaya.', + 'phone' => '018 2909252', + ], + 'lines' => $lines, + 'amount' => $invoice->amount + ]; + + $defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults(); + $fontDirs = $defaultConfig['fontDir']; + $defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults(); + $fontData = $defaultFontConfig['fontdata']; + + $mpdf = new \Mpdf\Mpdf([ + 'fontDir' => array_merge($fontDirs, [ + __DIR__ . '/custom/font/directory', + ]), + ]); + $mpdf->shrink_tables_to_fit=1; + $mpdf->keep_table_proportions = true; + $mpdf->SetTitle('Invoice'); + + $html = view('pdf/po', $data); + + $mpdf->Bookmark('Start of the document'); + $mpdf->SetHTMLFooter(' + + + + + +
This is generated by computer. No signature required.Page {PAGENO} of {nbpg}
+ '); + $mpdf->setHeader($invoice->po_number); + $mpdf->WriteHTML($html); + + $mpdf->Output(); + + } + + public function CIEFToSupplierDO(Request $request, $id) + { + + if (!is_numeric($id)) { + return response()->json(["message" => "Invalid id. Id needs to be a number"], 400); + } + + // Booking Exist and Belongs to User or is Admin + if ($request->user()->role === 'member') { + if (!$this->isUser($id, $request->user()->id)) { + return response()->json(['message' => 'The PO Does not belongs to you.'], 403); + } + } + + // Ensure Invoice Status is Approve + // if ($this->invoiceStatus($id) !== 'approve') { + // return response()->json(['message' => 'Invoice must and exist and approve status.'], 403); + // } + + // Generate PO + + $booking = Booking::where('id', $id)->first(); + $invoice = Invoice::where('booking_id', $booking['id'])->first(); + $lines = InvoiceDetails::where('invoice_id', $invoice->id)->get(); + $supplierbooking = SupplierBooking::where('booking_id', $id)->first(); + $supplier = SettingSupplier::where('id', $supplierbooking->supplier_id)->first(); + + $data = [ + 'title' => 'Delivery Order', + 'detail' => [ + 'date' => Carbon::createFromFormat('Y-m-d H:i:s', $booking->created_at)->format('d-m-Y'), + 'po' => $invoice->po_number, + 'do' => '', + 'ref' => $booking->id + ], + 'buyer' => [ + 'buyer_company' => 'CIEF Worldwide Sdn Bhd', + 'reg_no' => '1134596-M', + 'address' => 'Malaysia Global Innovation & Creativity Centre, Level + 1 CWS, Block 3730, Persiaran APEC 63000 + Cyberjaya.', + 'gst' => '', + 'phone' => '018 2909252', + 'marking_no' => '' + ], + 'seller' => [ + 'seller_company' => $supplier->company_name, + 'address' => '', + 'phone' => '', + ], + 'lines' => $lines, + 'amount' => $invoice->amount + ]; + $defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults(); + $fontDirs = $defaultConfig['fontDir']; + $defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults(); + $fontData = $defaultFontConfig['fontdata']; + + $mpdf = new \Mpdf\Mpdf([ + 'fontDir' => array_merge($fontDirs, [ + __DIR__ . '/custom/font/directory', + ]), + ]); + $mpdf->shrink_tables_to_fit=1; + $mpdf->keep_table_proportions = true; + $mpdf->SetTitle('Invoice'); + + $html = view('pdf/po', $data); + + $mpdf->Bookmark('Start of the document'); + $mpdf->SetHTMLFooter(' + + + + + +
This is generated by computer. No signature required.Page {PAGENO} of {nbpg}
+ '); + $mpdf->setHeader($invoice->po_number); + $mpdf->WriteHTML($html); + + $mpdf->Output(); + + } + + public function CIEFToCustomerInvoice(Request $request, $id) + { + + if (!is_numeric($id)) { + return response()->json(["message" => "Invalid id. Id needs to be a number"], 400); + } + + // only allow if belongs or is admin + if ($request->user()->role === 'member') { + if (!$this->isUser($id, $request->user()->id)) { + return response()->json(['message' => 'The Invoice Does not belongs to you.'], 403); + } + } + + $booking = Booking::where('id', $id)->first(); + $invoice = Invoice::where('booking_id', $booking['id'])->first(); + $lines = InvoiceDetails::where('invoice_id', $invoice->id)->get(); + + $data = [ + 'title' => 'Invoice', + 'detail' => [ + 'date' => Carbon::createFromFormat('Y-m-d H:i:s', $booking->created_at)->format('d-m-Y'), + 'ei' => $invoice->ei, + 'edo' => null, + 'ref' => $booking->id + ], + 'billto' => [ + 'buyer_company' => $invoice->buyer_company, + 'reg_no' => $invoice->reg_no, + 'address' => $invoice->address, + 'gst' => '', + 'phone' => $invoice->contact_no, + 'marking_no' => '' + ], + 'total_page' => (count($lines) / 10), + 'lines' => $lines, + 'amount' => $invoice->amount + ]; + + $defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults(); + $fontDirs = $defaultConfig['fontDir']; + $defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults(); + $fontData = $defaultFontConfig['fontdata']; + + $mpdf = new \Mpdf\Mpdf([ + 'fontDir' => array_merge($fontDirs, [ + __DIR__ . '/custom/font/directory', + ]), + ]); + $mpdf->shrink_tables_to_fit=1; + $mpdf->keep_table_proportions = true; + $mpdf->SetTitle('Invoice'); + + $html = view('pdf/invoice', $data); + + $mpdf->Bookmark('Start of the document'); + $mpdf->SetHTMLFooter(' + + + + + +
This is generated by computer. No signature required.Page {PAGENO} of {nbpg}
+ '); + $mpdf->setHeader($invoice->ei); + $mpdf->WriteHTML($html); + + $mpdf->Output(); + } + + public function CIEFToCustomerDO(Request $request, $id) + { + if (!is_numeric($id)) { + return response()->json(["message" => "Invalid id. Id needs to be a number"], 400); + } + + // only allow if belongs or is admin + if ($request->user()->role === 'member') { + if (!$this->isUser($id, $request->user()->id)) { + return response()->json(['message' => 'The Invoice Does not belongs to you.'], 403); + } + } + + // Generate PO + + $booking = Booking::where('id', $id)->first(); + $invoice = Invoice::where('booking_id', $booking['id'])->first(); + $lines = InvoiceDetails::where('invoice_id', $invoice->id)->get(); + + $data = [ + 'title' => 'Delivery Order', + 'detail' => [ + 'date' => Carbon::createFromFormat('Y-m-d H:i:s', $booking->created_at)->format('d-m-Y'), + 'ei' => '', + 'edo' => $invoice->edo, + 'ref' => $booking->id + ], + 'billto' => [ + 'buyer_company' => $invoice->buyer_company, + 'reg_no' => $invoice->reg_no, + 'address' => $invoice->address, + 'gst' => '', + 'phone' => $invoice->contact_no, + 'marking_no' => '' + ], + 'lines' => $lines, + 'amount' => $invoice->amount + ]; + + $defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults(); + $fontDirs = $defaultConfig['fontDir']; + $defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults(); + $fontData = $defaultFontConfig['fontdata']; + + $mpdf = new \Mpdf\Mpdf([ + 'fontDir' => array_merge($fontDirs, [ + __DIR__ . '/custom/font/directory', + ]), + ]); + $mpdf->shrink_tables_to_fit=1; + $mpdf->keep_table_proportions = true; + $mpdf->SetTitle('do'); + + $html = view('pdf/invoice', $data); + + $mpdf->Bookmark('Start of the document'); + $mpdf->SetHTMLFooter(' + + + + + +
This is generated by computer. No signature required.Page {PAGENO} of {nbpg}
+ '); + $mpdf->setHeader($invoice->edo); + $mpdf->WriteHTML($html); + + $mpdf->Output(); + } + + + private function isUser($booking_id, $user_id) { + + $booking = Booking::where([ + ['id', '=', $booking_id], + ['user_id', '=', $user_id] + ])->first(); + + if ($booking) { + return true; + } else { + return false; + } + } + + private function invoiceStatus($booking_id) { + $invoice = Invoice::where('booking_id', $booking_id)->first(); + if (!$invoice) { + return; + } + $currentstatus = InvoiceStatuses::where('invoice_id', $invoice['id'])->latest('created_at')->first()->status; + return $currentstatus; + } + +} diff --git a/app/Invoice.php b/app/Invoice.php index 5150b1e4..647b3bff 100644 --- a/app/Invoice.php +++ b/app/Invoice.php @@ -6,9 +6,17 @@ use Illuminate\Database\Eloquent\Model; class Invoice extends Model { - protected $fillable = ['invoice_url','amount','booking_id']; + protected $fillable = ['invoice_url','amount','booking_id', 'buyer_company', 'address', 'contact_no', 'po_number']; public function booking(){ - return $this->hasOne('App\Booking'); + return $this->belongsTo('App\Booking'); + } + + public function invoiceDetails() { + return $this->hasOne('App\InvoiceDetails'); + } + + public function invoiceStatuses() { + return $this->hasMany('App\InvoiceStatuses'); } } diff --git a/app/InvoiceDetails.php b/app/InvoiceDetails.php new file mode 100644 index 00000000..635d36e3 --- /dev/null +++ b/app/InvoiceDetails.php @@ -0,0 +1,14 @@ +belongsTo('App/Invoice'); + } +} diff --git a/app/InvoiceStatuses.php b/app/InvoiceStatuses.php new file mode 100644 index 00000000..a4b9b93b --- /dev/null +++ b/app/InvoiceStatuses.php @@ -0,0 +1,14 @@ +belongsTo('App/Invoice'); + } +} diff --git a/composer.json b/composer.json index 95740116..0954d4ee 100644 --- a/composer.json +++ b/composer.json @@ -6,6 +6,7 @@ "type": "project", "require": { "php": "^7.1.3", + "barryvdh/laravel-dompdf": "^0.8.6", "doctrine/dbal": "^2.7", "fideloper/proxy": "^4.0", "intervention/image": "^2.4", @@ -14,11 +15,13 @@ "laravel/tinker": "~1.0", "laravelcollective/html": "^5.6", "maatwebsite/excel": "^3.1", + "mpdf/mpdf": "^8.0", "pusher/pusher-php-server": "~3.0", "tymon/jwt-auth": "^1.0.0-rc.2", "zizaco/entrust": "dev-master" }, "require-dev": { + "beyondcode/laravel-er-diagram-generator": "^1.4", "filp/whoops": "^2.0", "fzaninotto/faker": "^1.4", "laravel/dusk": "^3.0", diff --git a/config/app.php b/config/app.php index 1c1d20d4..39628d0b 100644 --- a/config/app.php +++ b/config/app.php @@ -169,7 +169,9 @@ return [ App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, - Tymon\JWTAuth\Providers\LaravelServiceProvider::class + Tymon\JWTAuth\Providers\LaravelServiceProvider::class, + + Barryvdh\DomPDF\ServiceProvider::class, ], /* @@ -222,6 +224,7 @@ return [ 'Entrust' => Zizaco\Entrust\EntrustFacade::class, 'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class, 'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class, + 'PDF' => Barryvdh\DomPDF\Facade::class ], diff --git a/config/database.php b/config/database.php index 5633d24f..8fb874a2 100644 --- a/config/database.php +++ b/config/database.php @@ -43,9 +43,9 @@ return [ 'driver' => 'mysql', 'host' => env('DB_HOST', 'localhost'), 'port' => env('DB_PORT', '3306'), - 'database' => env('DB_DATABASE', 'forge'), - 'username' => env('DB_USERNAME', 'forge'), - 'password' => env('DB_PASSWORD', ''), + 'database' => env('DB_DATABASE', 'exchange'), + 'username' => env('DB_USERNAME', 'exchange'), + 'password' => env('DB_PASSWORD', 'exchange1!Q'), 'unix_socket' => env('DB_SOCKET', ''), 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', diff --git a/config/pdf.php b/config/pdf.php new file mode 100644 index 00000000..96554b86 --- /dev/null +++ b/config/pdf.php @@ -0,0 +1,24 @@ + 'utf-8', + 'format' => 'A4', + 'author' => '', + 'subject' => '', + 'keywords' => '', + 'creator' => 'Laravel Pdf', + 'display_mode' => 'fullpage', + 'tempDir' => base_path('../temp/'), + 'font_path' => base_path('resources/fonts/'), + 'font_data' => [ + 'examplefont' => [ + 'R' => 'ExampleFont-Regular.ttf', // regular font + 'B' => 'ExampleFont-Bold.ttf', // optional: bold font + 'I' => 'ExampleFont-Italic.ttf', // optional: italic font + 'BI' => 'ExampleFont-Bold-Italic.ttf', // optional: bold-italic font + 'useOTL' => 0xFF, // required for complicated langs like Persian, Arabic and Chinese + 'useKashida' => 75, // required for complicated langs like Persian, Arabic and Chinese + ] + // ...add as many as you want. + ] +]; diff --git a/database/migrations/2020_08_24_161747_add_fields_to_invoice.php b/database/migrations/2020_08_24_161747_add_fields_to_invoice.php new file mode 100644 index 00000000..df44829e --- /dev/null +++ b/database/migrations/2020_08_24_161747_add_fields_to_invoice.php @@ -0,0 +1,39 @@ +string('buyer_company', 50); + $table->string('reg_no', 100); + $table->string('address', 120); + $table->string('contact_no', 20); + $table->string('po_number', 20); + $table->string('order_no', 20); + $table->double('tax_rate')->default(0); + $table->double('billing_charge_rate'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + // + }); + } +} diff --git a/database/migrations/2020_08_24_170514_add_invoice_details.php b/database/migrations/2020_08_24_170514_add_invoice_details.php new file mode 100644 index 00000000..02dbb3b2 --- /dev/null +++ b/database/migrations/2020_08_24_170514_add_invoice_details.php @@ -0,0 +1,36 @@ +increments('id'); + $table->integer('order'); + $table->string('description'); + $table->integer('quantity'); + $table->double('unit_price'); + $table->string('stock_code'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('invoice_details'); + } +} diff --git a/database/migrations/2020_08_24_173533_add_foreign_id_to_invoice_details.php b/database/migrations/2020_08_24_173533_add_foreign_id_to_invoice_details.php new file mode 100644 index 00000000..99ab7f1c --- /dev/null +++ b/database/migrations/2020_08_24_173533_add_foreign_id_to_invoice_details.php @@ -0,0 +1,39 @@ +integer('invoice_id')->unsigned(); + + $table->foreign('invoice_id') + ->references('id') + ->on('invoices') + ->onDelete('cascade'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoice_details', function (Blueprint $table) { + // + }); + } +} diff --git a/database/migrations/2020_08_24_181820_create_invoice_status.php b/database/migrations/2020_08_24_181820_create_invoice_status.php new file mode 100644 index 00000000..86ad70f0 --- /dev/null +++ b/database/migrations/2020_08_24_181820_create_invoice_status.php @@ -0,0 +1,38 @@ +increments('id'); + $table->string('status', 20); + $table->string('comment'); + $table->timestamps(); + $table->integer('invoice_id')->unsigned(); + $table->foreign('invoice_id') + ->references('id') + ->on('invoices') + ->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('invoice_statuses'); + } +} diff --git a/database/migrations/2020_08_24_191227_add_default_value_invoices.php b/database/migrations/2020_08_24_191227_add_default_value_invoices.php new file mode 100644 index 00000000..2e68e896 --- /dev/null +++ b/database/migrations/2020_08_24_191227_add_default_value_invoices.php @@ -0,0 +1,41 @@ +string('buyer_company', 50)->default('')->change(); + $table->string('reg_no', 100)->default('')->change(); + $table->string('address', 120)->default('')->change(); + $table->string('contact_no', 20)->default('')->change(); + $table->string('po_number', 20)->default('')->change(); + $table->string('order_no', 20)->default('')->change(); + $table->float('tax_rate')->default(0)->change(); + $table->float('billing_charge_rate')->default(0)->change(); + $table->string('invoice_path')->default('')->change(); + $table->float('amount')->default(0)->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + // + }); + } +} diff --git a/database/migrations/2020_09_02_221821_remove_reg_no_order_no_invoices_table.php b/database/migrations/2020_09_02_221821_remove_reg_no_order_no_invoices_table.php new file mode 100644 index 00000000..b983f395 --- /dev/null +++ b/database/migrations/2020_09_02_221821_remove_reg_no_order_no_invoices_table.php @@ -0,0 +1,32 @@ +dropColumn(['reg_no', 'order_no', 'tax_rate', 'billing_charge_rate']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + // + }); + } +} diff --git a/database/migrations/2020_09_03_112820_remove_unit_price_add_unit_price_rmb_rm.php b/database/migrations/2020_09_03_112820_remove_unit_price_add_unit_price_rmb_rm.php new file mode 100644 index 00000000..291bbb4b --- /dev/null +++ b/database/migrations/2020_09_03_112820_remove_unit_price_add_unit_price_rmb_rm.php @@ -0,0 +1,34 @@ +dropColumn('unit_price'); + $table->double('unit_price_rmb'); + $table->double('unit_price_rm'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoice_details', function (Blueprint $table) { + // + }); + } +} diff --git a/database/migrations/2020_09_03_113336_add_total_invoice_details.php b/database/migrations/2020_09_03_113336_add_total_invoice_details.php new file mode 100644 index 00000000..6a929443 --- /dev/null +++ b/database/migrations/2020_09_03_113336_add_total_invoice_details.php @@ -0,0 +1,32 @@ +double('total'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoice_details', function (Blueprint $table) { + // + }); + } +} diff --git a/database/migrations/2020_09_04_151645_change_default_comment_null.php b/database/migrations/2020_09_04_151645_change_default_comment_null.php new file mode 100644 index 00000000..c0dd6db4 --- /dev/null +++ b/database/migrations/2020_09_04_151645_change_default_comment_null.php @@ -0,0 +1,32 @@ +string('comment')->default(null)->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoice_statuses', function (Blueprint $table) { + // + }); + } +} diff --git a/database/migrations/2020_09_04_152310_change_to_nullable_comment_null.php b/database/migrations/2020_09_04_152310_change_to_nullable_comment_null.php new file mode 100644 index 00000000..b05d43fa --- /dev/null +++ b/database/migrations/2020_09_04_152310_change_to_nullable_comment_null.php @@ -0,0 +1,32 @@ +string('comment')->default(null)->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoice_statuses', function (Blueprint $table) { + // + }); + } +} diff --git a/database/migrations/2020_09_07_024530_add_reg_no_to_invoices.php b/database/migrations/2020_09_07_024530_add_reg_no_to_invoices.php new file mode 100644 index 00000000..5dddf6c7 --- /dev/null +++ b/database/migrations/2020_09_07_024530_add_reg_no_to_invoices.php @@ -0,0 +1,32 @@ +string('reg_no'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + // + }); + } +} diff --git a/database/migrations/2020_09_11_221219_change_description_to_256_varchar.php b/database/migrations/2020_09_11_221219_change_description_to_256_varchar.php new file mode 100644 index 00000000..116830b2 --- /dev/null +++ b/database/migrations/2020_09_11_221219_change_description_to_256_varchar.php @@ -0,0 +1,32 @@ +string('description', 256)->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoice_details', function (Blueprint $table) { + // + }); + } +} diff --git a/database/migrations/2020_09_14_130855_add_ei_edo_epo_to_invoice.php b/database/migrations/2020_09_14_130855_add_ei_edo_epo_to_invoice.php new file mode 100644 index 00000000..06804003 --- /dev/null +++ b/database/migrations/2020_09_14_130855_add_ei_edo_epo_to_invoice.php @@ -0,0 +1,33 @@ +string('edo'); + $table->string('ei'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + $table->dropColumn(['edo', 'ei']); + }); + } +} diff --git a/database/migrations/2020_09_14_132221_add_default_null_to_edo_ei_and_nullable.php b/database/migrations/2020_09_14_132221_add_default_null_to_edo_ei_and_nullable.php new file mode 100644 index 00000000..b1e4367e --- /dev/null +++ b/database/migrations/2020_09_14_132221_add_default_null_to_edo_ei_and_nullable.php @@ -0,0 +1,33 @@ +string('edo')->default(null)->nullable()->change(); + $table->string('ei')->default(null)->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + // + }); + } +} diff --git a/database/migrations/2020_09_14_133932_remove_edo_ei.php b/database/migrations/2020_09_14_133932_remove_edo_ei.php new file mode 100644 index 00000000..b2d70fe3 --- /dev/null +++ b/database/migrations/2020_09_14_133932_remove_edo_ei.php @@ -0,0 +1,32 @@ +dropColumn(['edo', 'ei']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + // + }); + } +} diff --git a/database/migrations/2020_09_14_134204_create_edo_ei.php b/database/migrations/2020_09_14_134204_create_edo_ei.php new file mode 100644 index 00000000..81ee1127 --- /dev/null +++ b/database/migrations/2020_09_14_134204_create_edo_ei.php @@ -0,0 +1,33 @@ +string('ei')->default(null)->nullable()->unique(); + $table->string('edo')->default(null)->nullable()->unique(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoices', function (Blueprint $table) { + // + }); + } +} diff --git a/database/migrations/2020_09_14_143153_invoice_details_description_nullable.php b/database/migrations/2020_09_14_143153_invoice_details_description_nullable.php new file mode 100644 index 00000000..a1a890fa --- /dev/null +++ b/database/migrations/2020_09_14_143153_invoice_details_description_nullable.php @@ -0,0 +1,32 @@ +string('description', 256)->nullable()->default(null)->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('invoice_details', function (Blueprint $table) { + // + }); + } +} diff --git a/deployment-note.txt b/deployment-note.txt new file mode 100644 index 00000000..3fd72f53 --- /dev/null +++ b/deployment-note.txt @@ -0,0 +1,6 @@ +1. Run database migration +2. Run composer. New libraries have been added to generate pdf +3. Run npm install // I installed vuelidate for Vue Form validations +4. Run npm run prod // To build new vue files +5. Mbstring required to print chinese fonts. +6. Run composer. New libraries have been added to generate pdf. diff --git a/graph.png b/graph.png new file mode 100644 index 00000000..0fbfaee6 Binary files /dev/null and b/graph.png differ diff --git a/package.json b/package.json index d7f40569..872fba3a 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "vue-loading-spinner": "^1.0.11", "vue-meta": "^1.4.4", "vue-router": "^3.0.1", + "vuelidate": "^0.7.5", "vuex": "^3.0.1", "vuex-router-sync": "^5.0.0" }, diff --git a/public/fonts/vendor/element-ui/lib/theme-chalk/element-icons.ttf b/public/fonts/vendor/element-ui/lib/theme-chalk/element-icons.ttf index 73bc90f4..91b74de3 100644 Binary files a/public/fonts/vendor/element-ui/lib/theme-chalk/element-icons.ttf and b/public/fonts/vendor/element-ui/lib/theme-chalk/element-icons.ttf differ diff --git a/public/fonts/vendor/element-ui/lib/theme-chalk/element-icons.woff b/public/fonts/vendor/element-ui/lib/theme-chalk/element-icons.woff index 28da65d4..02b9a253 100644 Binary files a/public/fonts/vendor/element-ui/lib/theme-chalk/element-icons.woff and b/public/fonts/vendor/element-ui/lib/theme-chalk/element-icons.woff differ diff --git a/public/logo.png b/public/logo.png new file mode 100644 index 00000000..f9010050 Binary files /dev/null and b/public/logo.png differ diff --git a/public/mix-manifest.json b/public/mix-manifest.json index b0502809..bdcea994 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -1,7 +1,7 @@ { - "/js/lang-zh-CN.c0da3973e52421a8cfe5.js": "/js/lang-zh-CN.c0da3973e52421a8cfe5.js", - "/js/lang-es.f4a1a5b4a30e92527b46.js": "/js/lang-es.f4a1a5b4a30e92527b46.js", - "/js/lang-en.0f790217417404ecc662.js": "/js/lang-en.0f790217417404ecc662.js", + "/js/lang-zh-CN.301fa7c2a76971d5e1ff.js": "/js/lang-zh-CN.301fa7c2a76971d5e1ff.js", + "/js/lang-es.672a5851ff01fcf8d1b1.js": "/js/lang-es.672a5851ff01fcf8d1b1.js", + "/js/lang-en.3d0dc40deef054cb1338.js": "/js/lang-en.3d0dc40deef054cb1338.js", "/js/app.js": "/js/app.js", "/css/app.css": "/css/app.css" } diff --git a/resources/assets/js/components/NewInvoice.vue b/resources/assets/js/components/NewInvoice.vue new file mode 100644 index 00000000..570ed4b7 --- /dev/null +++ b/resources/assets/js/components/NewInvoice.vue @@ -0,0 +1,777 @@ +/* eslint-disable camelcase */ +