mirror of
				https://github.com/crater-invoice/crater.git
				synced 2025-10-30 13:11:08 -04:00 
			
		
		
		
	Compare commits
	
		
			77 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| bd5a93d81c | |||
| 858e10953b | |||
| 3617032735 | |||
| 75ddc51b1e | |||
| 1dfa36e396 | |||
| d9e9a5a540 | |||
| ea6e11c324 | |||
| f55dfe0b46 | |||
| 3eac3b8af5 | |||
| 2b2bd4351a | |||
| b9c32bbdc1 | |||
| bceffbf6a0 | |||
| 7c6a40374d | |||
| d04e142a3e | |||
| f11436736b | |||
| 9271ceba45 | |||
| c88eb24265 | |||
| 5eb0a04378 | |||
| d926073095 | |||
| a691969025 | |||
| 7df06fb005 | |||
| 45db850025 | |||
| 18a50315ba | |||
| bce1b4bb3e | |||
| ddd204105f | |||
| e030d4b9d0 | |||
| 302968225a | |||
| e59bf288ce | |||
| b1fcd90b62 | |||
| ddb0ff1b8a | |||
| ce99fa3d82 | |||
| e28c89085d | |||
| 34f7e33abc | |||
| 80be7a492d | |||
| 387cb4490d | |||
| 09829a559e | |||
| b06fc5f0b9 | |||
| 5f7401f622 | |||
| 4f6dae919b | |||
| 36be395579 | |||
| 79e77f9e16 | |||
| cbf0af2120 | |||
| 92f754e888 | |||
| a6896eaa01 | |||
| 01f3646869 | |||
| b2918e9dbb | |||
| 0a064ec5ba | |||
| 5f0b4b3496 | |||
| 17b59f0d19 | |||
| 283b910cc3 | |||
| 81739827c0 | |||
| 90edc3a85e | |||
| 655c2a7849 | |||
| ca833d174e | |||
| 33e8381fc4 | |||
| 9b5125d440 | |||
| 2899021804 | |||
| d8f6d03d1e | |||
| c474e98925 | |||
| 9424dc6c27 | |||
| 3692373cd2 | |||
| 7bba576dca | |||
| 05454af593 | |||
| 74fe481ed5 | |||
| 1cd654b0cc | |||
| 0c71356f59 | |||
| e539bb501d | |||
| 2fcd169270 | |||
| 14d71fedb3 | |||
| 7fe9a4c2a2 | |||
| 7b697a477e | |||
| 146cf835b9 | |||
| ec87e72547 | |||
| bf2e8c9c99 | |||
| b6096aadfa | |||
| 1cbc41c3ce | |||
| 8bc5ea2d5e | 
							
								
								
									
										10
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| .dockerignore | ||||
| .gitignore | ||||
| *.md | ||||
| .git/ | ||||
| .idea/ | ||||
| .DS_Store/ | ||||
| docker-compose.* | ||||
| LICENSE | ||||
| nginx.conf | ||||
| yarn.lock | ||||
| @ -1,15 +1,15 @@ | ||||
| APP_ENV=production | ||||
| APP_KEY=base64:kgk/4DW1vEVy7aEvet5FPp5un6PIGe/so8H0mvoUtW0= | ||||
| APP_DEBUG=false | ||||
| APP_DEBUG=true | ||||
| APP_LOG_LEVEL=debug | ||||
| APP_URL=http://crater.test | ||||
|  | ||||
| DB_CONNECTION=mysql | ||||
| DB_HOST=127.0.0.1 | ||||
| DB_HOST=db | ||||
| DB_PORT=3306 | ||||
| DB_DATABASE=crater | ||||
| DB_USERNAME=root | ||||
| DB_PASSWORD=bytefury | ||||
| DB_USERNAME=crater | ||||
| DB_PASSWORD=crater | ||||
|  | ||||
| BROADCAST_DRIVER=log | ||||
| CACHE_DRIVER=file | ||||
|  | ||||
							
								
								
									
										26
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| --- | ||||
| name: Bug report | ||||
| about: Create a report to help us improve | ||||
| title: '' | ||||
| labels: '' | ||||
| assignees: '' | ||||
|  | ||||
| --- | ||||
|  | ||||
| **Describe the bug** | ||||
| A clear and concise description of what the bug is. | ||||
|  | ||||
| **Expected behavior** | ||||
| A clear and concise description of what you expected to happen. | ||||
|  | ||||
| **Screenshots** | ||||
| If applicable, add screenshots to help explain your problem. | ||||
|  | ||||
| **Please complete the following information:** | ||||
| - Crater version:  | ||||
| - PHP version:  | ||||
| - Database type and version:  | ||||
|  | ||||
| **Optional info** | ||||
| - OS:  [e.g. Ubuntu] | ||||
| - Browser: [e.g. chrome, safari] | ||||
							
								
								
									
										17
									
								
								.github/ISSUE_TEMPLATE/feature_request.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.github/ISSUE_TEMPLATE/feature_request.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| --- | ||||
| name: Feature request | ||||
| about: Suggest an idea for this project | ||||
| title: '' | ||||
| labels: '' | ||||
| assignees: '' | ||||
|  | ||||
| --- | ||||
|  | ||||
| **Is your feature request related to a problem? Please describe.** | ||||
| A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] | ||||
|  | ||||
| **Describe the solution you'd like** | ||||
| A clear and concise description of what you want to happen. | ||||
|  | ||||
| **Describe alternatives you've considered** | ||||
| A clear and concise description of any alternative solutions or features you've considered. | ||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -11,3 +11,5 @@ Homestead.yaml | ||||
| .rnd | ||||
| /.expo | ||||
| /.vscode | ||||
| docker-compose.yml | ||||
| docker-compose.yaml | ||||
|  | ||||
							
								
								
									
										52
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| ##### STAGE 1 ##### | ||||
|  | ||||
| FROM composer as composer | ||||
|  | ||||
| # Copy composer files from project root into composer container's working dir | ||||
| COPY composer.* /app/ | ||||
|  | ||||
| # Copy database directory for autoloader optimization | ||||
| COPY database /app/database | ||||
|  | ||||
| # Run composer to build dependencies in vendor folder | ||||
| RUN composer install --no-scripts --no-suggest --no-interaction --prefer-dist --optimize-autoloader  | ||||
|  | ||||
| # Copy everything from project root into composer container's working dir | ||||
| COPY . /app | ||||
|   | ||||
| RUN composer dump-autoload --optimize --classmap-authoritative | ||||
|  | ||||
| ##### STAGE 2 ##### | ||||
|  | ||||
| FROM php:7.3.12-fpm-alpine | ||||
|  | ||||
| # Use the default production configuration | ||||
| RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" | ||||
|  | ||||
| RUN apk add --no-cache libpng-dev libxml2-dev oniguruma-dev && \ | ||||
|     docker-php-ext-install bcmath ctype json gd mbstring pdo pdo_mysql tokenizer xml | ||||
|  | ||||
| # Set container's working dir | ||||
| WORKDIR /app | ||||
|   | ||||
| # Copy everything from project root into php container's working dir | ||||
| COPY . /app | ||||
|  | ||||
| # Copy vendor folder from composer container into php container | ||||
| COPY --from=composer /app/vendor /app/vendor | ||||
|  | ||||
| RUN touch database/database.sqlite && \ | ||||
|     cp .env.example .env && \ | ||||
|     php artisan config:cache && \ | ||||
|     php artisan passport:keys && \ | ||||
|     php artisan key:generate && \ | ||||
|     chown -R www-data:www-data . && \ | ||||
|     chmod -R 755 . && \ | ||||
|     chmod -R 775 storage/framework/ && \ | ||||
|     chmod -R 775 storage/logs/ && \ | ||||
|     chmod -R 775 bootstrap/cache/   | ||||
|  | ||||
| EXPOSE 9000 | ||||
|  | ||||
| CMD ["php-fpm", "--nodaemonize"] | ||||
|  | ||||
| @ -49,15 +49,20 @@ class Estimate extends Model | ||||
|     ]; | ||||
|  | ||||
|     protected $casts = [ | ||||
|         'total' => 'float', | ||||
|         'tax' => 'float', | ||||
|         'sub_total' => 'float' | ||||
|         'total' => 'integer', | ||||
|         'tax' => 'integer', | ||||
|         'sub_total' => 'integer', | ||||
|         'discount' => 'float', | ||||
|         'discount_val' => 'integer', | ||||
|     ]; | ||||
|  | ||||
|     public static function getNextEstimateNumber() | ||||
|     public static function getNextEstimateNumber($value) | ||||
|     { | ||||
|          // Get the last created order | ||||
|         $lastOrder = Estimate::orderBy('created_at', 'desc')->first(); | ||||
|          $lastOrder = Estimate::where('estimate_number', 'LIKE', $value . '-%') | ||||
|                         ->orderBy('created_at', 'desc') | ||||
|                         ->first(); | ||||
|  | ||||
|         if (!$lastOrder) { | ||||
|             // We get here if there is no order at all | ||||
|             // If there is no number set it to 0, which will be 1 at the end. | ||||
| @ -99,10 +104,16 @@ class Estimate extends Model | ||||
|  | ||||
|     public function getEstimateNumAttribute() | ||||
|     { | ||||
|         $position = $this->strposX($this->estimate_number, "-", 2) + 1; | ||||
|         $position = $this->strposX($this->estimate_number, "-", 1) + 1; | ||||
|         return substr($this->estimate_number, $position); | ||||
|     } | ||||
|  | ||||
|     public function getEstimatePrefixAttribute() | ||||
|     { | ||||
|         $prefix = explode("-",$this->estimate_number)[0]; | ||||
|         return $prefix; | ||||
|     } | ||||
|  | ||||
|     private function strposX($haystack, $needle, $number) | ||||
|     { | ||||
|         if ($number == '1') { | ||||
|  | ||||
| @ -153,6 +153,58 @@ class CompanyController extends Controller | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function getCustomizeSetting (Request $request) | ||||
|     { | ||||
|         $invoice_prefix = CompanySetting::getSetting('invoice_prefix', $request->header('company')); | ||||
|         $invoice_auto_generate = CompanySetting::getSetting('invoice_auto_generate', $request->header('company')); | ||||
|  | ||||
|         $estimate_prefix = CompanySetting::getSetting('estimate_prefix', $request->header('company')); | ||||
|         $estimate_auto_generate  = CompanySetting::getSetting('estimate_auto_generate', $request->header('company')); | ||||
|  | ||||
|         $payment_prefix = CompanySetting::getSetting('payment_prefix', $request->header('company')); | ||||
|         $payment_auto_generate = CompanySetting::getSetting('payment_auto_generate', $request->header('company')); | ||||
|  | ||||
|         return  response()->json([ | ||||
|             'invoice_prefix' => $invoice_prefix, | ||||
|             'invoice_auto_generate' => $invoice_auto_generate, | ||||
|             'estimate_prefix' => $estimate_prefix, | ||||
|             'estimate_auto_generate' => $estimate_auto_generate, | ||||
|             'payment_prefix' => $payment_prefix, | ||||
|             'payment_auto_generate' => $payment_auto_generate, | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function updateCustomizeSetting (Request $request) | ||||
|     { | ||||
|         $sets = []; | ||||
|  | ||||
|         if ($request->type == "PAYMENTS") { | ||||
|             $sets = [ | ||||
|                 'payment_prefix' | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         if ($request->type == "INVOICES") { | ||||
|             $sets = [ | ||||
|                 'invoice_prefix', | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         if ($request->type == "ESTIMATES") { | ||||
|             $sets = [ | ||||
|                 'estimate_prefix', | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         foreach ($sets as $key) { | ||||
|             CompanySetting::setSetting($key, $request->$key, $request->header('company')); | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'success' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function updateSetting(SettingRequest $request) | ||||
|     { | ||||
|         CompanySetting::setSetting($request->key, $request->value, $request->header('company')); | ||||
|  | ||||
| @ -177,6 +177,7 @@ class CustomersController extends Controller | ||||
|         $customer->enable_portal = $request->enable_portal; | ||||
|         $customer->save(); | ||||
|  | ||||
|         $customer->addresses()->delete(); | ||||
|         if ($request->addresses) { | ||||
|             foreach ($request->addresses as $address) { | ||||
|                 $newAddress = $customer->addresses()->firstOrNew(['type' => $address["type"]]); | ||||
|  | ||||
| @ -56,25 +56,43 @@ class EstimatesController extends Controller | ||||
|  | ||||
|     public function create(Request $request) | ||||
|     { | ||||
|         $nextEstimateNumber = 'EST-'.Estimate::getNextEstimateNumber(); | ||||
|         $estimate_prefix = CompanySetting::getSetting('estimate_prefix', $request->header('company')); | ||||
|         $estimate_num_auto_generate = CompanySetting::getSetting('estimate_auto_generate', $request->header('company')); | ||||
|  | ||||
|         $nextEstimateNumberAttribute = null; | ||||
|         $nextEstimateNumber = Estimate::getNextEstimateNumber($estimate_prefix); | ||||
|  | ||||
|         if ($estimate_num_auto_generate == "YES") { | ||||
|             $nextEstimateNumberAttribute = $nextEstimateNumber; | ||||
|         } | ||||
|  | ||||
|         $tax_per_item = CompanySetting::getSetting('tax_per_item', $request->header('company')); | ||||
|         $discount_per_item = CompanySetting::getSetting('discount_per_item', $request->header('company')); | ||||
|         $customers = User::where('role', 'customer')->get(); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'customers' => $customers, | ||||
|             'nextEstimateNumber' => $nextEstimateNumber, | ||||
|             'nextEstimateNumberAttribute' => $nextEstimateNumberAttribute, | ||||
|             'nextEstimateNumber' => $estimate_prefix.'-'.$nextEstimateNumber, | ||||
|             'taxes' => Tax::whereCompany($request->header('company'))->latest()->get(), | ||||
|             'items' => Item::whereCompany($request->header('company'))->get(), | ||||
|             'tax_per_item' => $tax_per_item, | ||||
|             'discount_per_item' => $discount_per_item, | ||||
|             'estimateTemplates' => EstimateTemplate::all(), | ||||
|             'shareable_link' => '' | ||||
|             'shareable_link' => '', | ||||
|             'estimate_prefix' => $estimate_prefix | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function store(EstimatesRequest $request) | ||||
|     { | ||||
|         $estimate_number = explode("-",$request->estimate_number); | ||||
|         $number_attributes['estimate_number'] = $estimate_number[0].'-'.sprintf('%06d', intval($estimate_number[1])); | ||||
|  | ||||
|         Validator::make($number_attributes, [ | ||||
|             'estimate_number' => 'required|unique:estimates,estimate_number' | ||||
|         ])->validate(); | ||||
|  | ||||
|         $estimate_date = Carbon::createFromFormat('d/m/Y', $request->estimate_date); | ||||
|         $expiry_date = Carbon::createFromFormat('d/m/Y', $request->expiry_date); | ||||
|         $status = Estimate::STATUS_DRAFT; | ||||
| @ -101,7 +119,7 @@ class EstimatesController extends Controller | ||||
|         $estimate = Estimate::create([ | ||||
|             'estimate_date' => $estimate_date, | ||||
|             'expiry_date' => $expiry_date, | ||||
|             'estimate_number' => $request->estimate_number, | ||||
|             'estimate_number' => $number_attributes['estimate_number'], | ||||
|             'reference_number' => $request->reference_number, | ||||
|             'user_id' => $request->user_id, | ||||
|             'company_id' => $request->header('company'), | ||||
| @ -127,7 +145,7 @@ class EstimatesController extends Controller | ||||
|  | ||||
|             if (array_key_exists('taxes', $estimateItem) && $estimateItem['taxes']) { | ||||
|                 foreach ($estimateItem['taxes'] as $tax) { | ||||
|                     if ($tax['amount']) { | ||||
|                     if (gettype($tax['amount']) !== "NULL") { | ||||
|                         $tax['company_id'] = $request->header('company'); | ||||
|                         $item->taxes()->create($tax); | ||||
|                     } | ||||
| @ -137,7 +155,7 @@ class EstimatesController extends Controller | ||||
|  | ||||
|         if ($request->has('taxes')) { | ||||
|             foreach ($request->taxes as $tax) { | ||||
|                 if ($tax['amount']) { | ||||
|                 if (gettype($tax['amount']) !== "NULL") { | ||||
|                     $tax['company_id'] = $request->header('company'); | ||||
|                     $estimate->taxes()->create($tax); | ||||
|                 } | ||||
| @ -216,26 +234,33 @@ class EstimatesController extends Controller | ||||
|  | ||||
|         return response()->json( [ | ||||
|             'customers' => $customers, | ||||
|             'nextEstimateNumber' => $estimate->estimate_number, | ||||
|             'nextEstimateNumber' => $estimate->getEstimateNumAttribute(), | ||||
|             'taxes' => Tax::latest()->whereCompany($request->header('company'))->get(), | ||||
|             'estimate' => $estimate, | ||||
|             'items' => Item::whereCompany($request->header('company'))->latest()->get(), | ||||
|             'estimateTemplates' => EstimateTemplate::all(), | ||||
|             'tax_per_item' => $estimate->tax_per_item, | ||||
|             'discount_per_item' => $estimate->discount_per_item, | ||||
|             'shareable_link' => url('/estimates/pdf/'.$estimate->unique_hash) | ||||
|             'shareable_link' => url('/estimates/pdf/'.$estimate->unique_hash), | ||||
|             'estimate_prefix' => $estimate->getEstimatePrefixAttribute() | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function update(EstimatesRequest $request, $id) | ||||
|     { | ||||
|         $estimate_number = explode("-",$request->estimate_number); | ||||
|         $number_attributes['estimate_number'] = $estimate_number[0].'-'.sprintf('%06d', intval($estimate_number[1])); | ||||
|         Validator::make($number_attributes, [ | ||||
|             'estimate_number' => 'required|unique:estimates,estimate_number'.','.$id | ||||
|         ])->validate(); | ||||
|  | ||||
|         $estimate_date = Carbon::createFromFormat('d/m/Y', $request->estimate_date); | ||||
|         $expiry_date = Carbon::createFromFormat('d/m/Y', $request->expiry_date); | ||||
|  | ||||
|         $estimate = Estimate::find($id); | ||||
|         $estimate->estimate_date = $estimate_date; | ||||
|         $estimate->expiry_date = $expiry_date; | ||||
|         $estimate->estimate_number = $request->estimate_number; | ||||
|         $estimate->estimate_number = $number_attributes['estimate_number']; | ||||
|         $estimate->reference_number = $request->reference_number; | ||||
|         $estimate->user_id = $request->user_id; | ||||
|         $estimate->estimate_template_id = $request->estimate_template_id; | ||||
| @ -266,7 +291,7 @@ class EstimatesController extends Controller | ||||
|  | ||||
|             if (array_key_exists('taxes', $estimateItem) && $estimateItem['taxes']) { | ||||
|                 foreach ($estimateItem['taxes'] as $tax) { | ||||
|                     if ($tax['amount']) { | ||||
|                     if (gettype($tax['amount']) !== "NULL") { | ||||
|                         $tax['company_id'] = $request->header('company'); | ||||
|                         $item->taxes()->create($tax); | ||||
|                     } | ||||
| @ -276,7 +301,7 @@ class EstimatesController extends Controller | ||||
|  | ||||
|         if ($request->has('taxes')) { | ||||
|             foreach ($request->taxes as $tax) { | ||||
|                 if ($tax['amount']) { | ||||
|                 if (gettype($tax['amount']) !== "NULL") { | ||||
|                     $tax['company_id'] = $request->header('company'); | ||||
|                     $estimate->taxes()->create($tax); | ||||
|                 } | ||||
|  | ||||
| @ -66,14 +66,24 @@ class InvoicesController extends Controller | ||||
|     { | ||||
|         $tax_per_item = CompanySetting::getSetting('tax_per_item', $request->header('company')); | ||||
|         $discount_per_item = CompanySetting::getSetting('discount_per_item', $request->header('company')); | ||||
|         $nextInvoiceNumber = "INV-".Invoice::getNextInvoiceNumber(); | ||||
|         $invoice_prefix = CompanySetting::getSetting('invoice_prefix', $request->header('company')); | ||||
|         $invoice_num_auto_generate = CompanySetting::getSetting('invoice_auto_generate', $request->header('company')); | ||||
|  | ||||
|         $nextInvoiceNumberAttribute = null; | ||||
|         $nextInvoiceNumber = Invoice::getNextInvoiceNumber($invoice_prefix); | ||||
|  | ||||
|         if ($invoice_num_auto_generate == "YES") { | ||||
|             $nextInvoiceNumberAttribute = $nextInvoiceNumber; | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'nextInvoiceNumber' => $nextInvoiceNumber, | ||||
|             'nextInvoiceNumberAttribute' => $nextInvoiceNumberAttribute, | ||||
|             'nextInvoiceNumber' => $invoice_prefix.'-'.$nextInvoiceNumber, | ||||
|             'items' => Item::with('taxes')->whereCompany($request->header('company'))->get(), | ||||
|             'invoiceTemplates' => InvoiceTemplate::all(), | ||||
|             'tax_per_item' => $tax_per_item, | ||||
|             'discount_per_item' => $discount_per_item | ||||
|             'discount_per_item' => $discount_per_item, | ||||
|             'invoice_prefix' => $invoice_prefix | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
| @ -85,6 +95,13 @@ class InvoicesController extends Controller | ||||
|      */ | ||||
|     public function store(Requests\InvoicesRequest $request) | ||||
|     { | ||||
|         $invoice_number = explode("-",$request->invoice_number); | ||||
|         $number_attributes['invoice_number'] = $invoice_number[0].'-'.sprintf('%06d', intval($invoice_number[1])); | ||||
|  | ||||
|         Validator::make($number_attributes, [ | ||||
|             'invoice_number' => 'required|unique:invoices,invoice_number' | ||||
|         ])->validate(); | ||||
|  | ||||
|         $invoice_date = Carbon::createFromFormat('d/m/Y', $request->invoice_date); | ||||
|         $due_date = Carbon::createFromFormat('d/m/Y', $request->due_date); | ||||
|         $status = Invoice::STATUS_DRAFT; | ||||
| @ -99,7 +116,7 @@ class InvoicesController extends Controller | ||||
|         $invoice = Invoice::create([ | ||||
|             'invoice_date' => $invoice_date, | ||||
|             'due_date' => $due_date, | ||||
|             'invoice_number' => $request->invoice_number, | ||||
|             'invoice_number' => $number_attributes['invoice_number'], | ||||
|             'reference_number' => $request->reference_number, | ||||
|             'user_id' => $request->user_id, | ||||
|             'company_id' => $request->header('company'), | ||||
| @ -128,8 +145,7 @@ class InvoicesController extends Controller | ||||
|             if (array_key_exists('taxes', $invoiceItem) && $invoiceItem['taxes']) { | ||||
|                 foreach ($invoiceItem['taxes'] as $tax) { | ||||
|                     $tax['company_id'] = $request->header('company'); | ||||
|  | ||||
|                     if ($tax['amount']) { | ||||
|                     if (gettype($tax['amount']) !== "NULL") { | ||||
|                         $item->taxes()->create($tax); | ||||
|                     } | ||||
|                 } | ||||
| @ -140,7 +156,7 @@ class InvoicesController extends Controller | ||||
|             foreach ($request->taxes as $tax) { | ||||
|                 $tax['company_id'] = $request->header('company'); | ||||
|  | ||||
|                 if ($tax['amount']) { | ||||
|                 if (gettype($tax['amount']) !== "NULL") { | ||||
|                     $invoice->taxes()->create($tax); | ||||
|                 } | ||||
|             } | ||||
| @ -222,12 +238,13 @@ class InvoicesController extends Controller | ||||
|         ])->find($id); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'nextInvoiceNumber' => $invoice->invoice_number, | ||||
|             'nextInvoiceNumber' => $invoice->getInvoiceNumAttribute(), | ||||
|             'invoice' => $invoice, | ||||
|             'invoiceTemplates' => InvoiceTemplate::all(), | ||||
|             'tax_per_item' => $invoice->tax_per_item, | ||||
|             'discount_per_item' => $invoice->discount_per_item, | ||||
|             'shareable_link' => url('/invoices/pdf/'.$invoice->unique_hash) | ||||
|             'shareable_link' => url('/invoices/pdf/'.$invoice->unique_hash), | ||||
|             'invoice_prefix' => $invoice->getInvoicePrefixAttribute() | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
| @ -240,6 +257,13 @@ class InvoicesController extends Controller | ||||
|      */ | ||||
|     public function update(Requests\InvoicesRequest $request, $id) | ||||
|     { | ||||
|         $invoice_number = explode("-",$request->invoice_number); | ||||
|         $number_attributes['invoice_number'] = $invoice_number[0].'-'.sprintf('%06d', intval($invoice_number[1])); | ||||
|  | ||||
|         Validator::make($number_attributes, [ | ||||
|             'invoice_number' => 'required|unique:invoices,invoice_number'.','.$id | ||||
|         ])->validate(); | ||||
|  | ||||
|         $invoice_date = Carbon::createFromFormat('d/m/Y', $request->invoice_date); | ||||
|         $due_date = Carbon::createFromFormat('d/m/Y', $request->due_date); | ||||
|  | ||||
| @ -268,7 +292,7 @@ class InvoicesController extends Controller | ||||
|  | ||||
|         $invoice->invoice_date = $invoice_date; | ||||
|         $invoice->due_date = $due_date; | ||||
|         $invoice->invoice_number = $request->invoice_number; | ||||
|         $invoice->invoice_number =  $number_attributes['invoice_number']; | ||||
|         $invoice->reference_number = $request->reference_number; | ||||
|         $invoice->user_id = $request->user_id; | ||||
|         $invoice->invoice_template_id = $request->invoice_template_id; | ||||
| @ -292,7 +316,6 @@ class InvoicesController extends Controller | ||||
|         foreach ($oldTaxes as $oldTax) { | ||||
|             Tax::destroy($oldTax['id']); | ||||
|         } | ||||
|  | ||||
|         foreach ($invoiceItems as $invoiceItem) { | ||||
|             $invoiceItem['company_id'] = $request->header('company'); | ||||
|             $item = $invoice->items()->create($invoiceItem); | ||||
| @ -300,8 +323,7 @@ class InvoicesController extends Controller | ||||
|             if (array_key_exists('taxes', $invoiceItem) && $invoiceItem['taxes']) { | ||||
|                 foreach ($invoiceItem['taxes'] as $tax) { | ||||
|                     $tax['company_id'] = $request->header('company'); | ||||
|  | ||||
|                     if ($tax['amount']) { | ||||
|                     if (gettype($tax['amount']) !== "NULL") { | ||||
|                         $item->taxes()->create($tax); | ||||
|                     } | ||||
|                 } | ||||
| @ -312,7 +334,7 @@ class InvoicesController extends Controller | ||||
|             foreach ($request->taxes as $tax) { | ||||
|                 $tax['company_id'] = $request->header('company'); | ||||
|  | ||||
|                 if ($tax['amount']) { | ||||
|                 if (gettype($tax['amount']) !== "NULL") { | ||||
|                     $invoice->taxes()->create($tax); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @ -202,6 +202,45 @@ class OnboardingController extends Controller | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         $invoices = [ | ||||
|             'invoice_auto_generate' => 'YES', | ||||
|             'invoice_prefix' => 'INV' | ||||
|         ]; | ||||
|  | ||||
|         foreach ($invoices as $key => $value) { | ||||
|             CompanySetting::setSetting( | ||||
|                 $key, | ||||
|                 $value, | ||||
|                 $user->company_id | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         $estimates = [ | ||||
|             'estimate_prefix' => 'EST', | ||||
|             'estimate_auto_generate' => 'YES' | ||||
|         ]; | ||||
|  | ||||
|         foreach ($estimates as $key => $value) { | ||||
|             CompanySetting::setSetting( | ||||
|                 $key, | ||||
|                 $value, | ||||
|                 $user->company_id | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         $payments = [ | ||||
|             'payment_prefix' => 'PAY', | ||||
|             'payment_auto_generate' => 'YES' | ||||
|         ]; | ||||
|  | ||||
|         foreach ($payments as $key => $value) { | ||||
|             CompanySetting::setSetting( | ||||
|                 $key, | ||||
|                 $value, | ||||
|                 $user->company_id | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         $colors = [ | ||||
|             'primary_text_color' => '#5851D8', | ||||
|             'heading_text_color' => '#595959', | ||||
| @ -238,7 +277,14 @@ class OnboardingController extends Controller | ||||
|  | ||||
|         if (file_exists($path)) { | ||||
|             file_put_contents($path, str_replace( | ||||
|                 'PROXY_OAUTH_CLIENT_SECRET='.config('auth.proxy.client_secret'), 'PROXY_OAUTH_CLIENT_SECRET='.$client->secret, file_get_contents($path) | ||||
|                 'PROXY_OAUTH_CLIENT_SECRET='.config('auth.proxy.client_secret'), | ||||
|                 'PROXY_OAUTH_CLIENT_SECRET='.$client->secret, | ||||
|                 file_get_contents($path) | ||||
|             )); | ||||
|             file_put_contents($path, str_replace( | ||||
|                 'APP_DEBUG=true', | ||||
|                 'APP_DEBUG=false', | ||||
|                 file_get_contents($path) | ||||
|             )); | ||||
|         } | ||||
|  | ||||
|  | ||||
| @ -10,6 +10,7 @@ use Carbon\Carbon; | ||||
| use function MongoDB\BSON\toJSON; | ||||
| use Crater\User; | ||||
| use Crater\Http\Requests\PaymentRequest; | ||||
| use Validator; | ||||
|  | ||||
| class PaymentController extends Controller | ||||
| { | ||||
| @ -50,13 +51,24 @@ class PaymentController extends Controller | ||||
|      */ | ||||
|     public function create(Request $request) | ||||
|     { | ||||
|         $nextPaymentNumber = 'PAY-'.Payment::getNextPaymentNumber(); | ||||
|         $payment_prefix = CompanySetting::getSetting('payment_prefix', $request->header('company')); | ||||
|         $payment_num_auto_generate = CompanySetting::getSetting('payment_auto_generate', $request->header('company')); | ||||
|  | ||||
|  | ||||
|         $nextPaymentNumberAttribute = null; | ||||
|         $nextPaymentNumber = Payment::getNextPaymentNumber($payment_prefix); | ||||
|  | ||||
|         if ($payment_num_auto_generate == "YES") { | ||||
|             $nextPaymentNumberAttribute = $nextPaymentNumber; | ||||
|         } | ||||
|  | ||||
|         return response()->json([ | ||||
|             'customers' => User::where('role', 'customer') | ||||
|                 ->whereCompany($request->header('company')) | ||||
|                 ->get(), | ||||
|             'nextPaymentNumber' => $nextPaymentNumber | ||||
|             'nextPaymentNumberAttribute' => $nextPaymentNumberAttribute, | ||||
|             'nextPaymentNumber' => $payment_prefix.'-'.$nextPaymentNumber, | ||||
|             'payment_prefix' => $payment_prefix | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
| @ -68,6 +80,13 @@ class PaymentController extends Controller | ||||
|      */ | ||||
|     public function store(PaymentRequest $request) | ||||
|     { | ||||
|         $payment_number = explode("-",$request->payment_number); | ||||
|         $number_attributes['payment_number'] = $payment_number[0].'-'.sprintf('%06d', intval($payment_number[1])); | ||||
|  | ||||
|         Validator::make($number_attributes, [ | ||||
|             'payment_number' => 'required|unique:payments,payment_number' | ||||
|         ])->validate(); | ||||
|  | ||||
|         $payment_date = Carbon::createFromFormat('d/m/Y', $request->payment_date); | ||||
|  | ||||
|         if ($request->has('invoice_id') && $request->invoice_id != null) { | ||||
| @ -90,7 +109,7 @@ class PaymentController extends Controller | ||||
|  | ||||
|         $payment = Payment::create([ | ||||
|             'payment_date' => $payment_date, | ||||
|             'payment_number' => $request->payment_number, | ||||
|             'payment_number' => $number_attributes['payment_number'], | ||||
|             'user_id' => $request->user_id, | ||||
|             'company_id' => $request->header('company'), | ||||
|             'invoice_id' => $request->invoice_id, | ||||
| @ -135,7 +154,8 @@ class PaymentController extends Controller | ||||
|             'customers' => User::where('role', 'customer') | ||||
|                 ->whereCompany($request->header('company')) | ||||
|                 ->get(), | ||||
|             'nextPaymentNumber' => $payment->payment_number, | ||||
|             'nextPaymentNumber' => $payment->getPaymentNumAttribute(), | ||||
|             'payment_prefix' => $payment->getPaymentPrefixAttribute(), | ||||
|             'payment' => $payment, | ||||
|             'invoices' => $invoices | ||||
|         ]); | ||||
| @ -150,6 +170,13 @@ class PaymentController extends Controller | ||||
|      */ | ||||
|     public function update(PaymentRequest $request, $id) | ||||
|     { | ||||
|         $payment_number = explode("-",$request->payment_number); | ||||
|         $number_attributes['payment_number'] = $payment_number[0].'-'.sprintf('%06d', intval($payment_number[1])); | ||||
|  | ||||
|         Validator::make($number_attributes, [ | ||||
|             'payment_number' => 'required|unique:payments,payment_number'.','.$id | ||||
|         ])->validate(); | ||||
|  | ||||
|         $payment_date = Carbon::createFromFormat('d/m/Y', $request->payment_date); | ||||
|  | ||||
|         $payment = Payment::find($id); | ||||
| @ -178,7 +205,7 @@ class PaymentController extends Controller | ||||
|         } | ||||
|  | ||||
|         $payment->payment_date = $payment_date; | ||||
|         $payment->payment_number = $request->payment_number; | ||||
|         $payment->payment_number = $number_attributes['payment_number']; | ||||
|         $payment->user_id = $request->user_id; | ||||
|         $payment->invoice_id = $request->invoice_id; | ||||
|         $payment->payment_mode = $request->payment_mode; | ||||
|  | ||||
| @ -25,7 +25,6 @@ class EstimatesRequest extends FormRequest | ||||
|         $rules = [ | ||||
|             'estimate_date' => 'required', | ||||
|             'expiry_date' => 'required', | ||||
|             'estimate_number' => 'required|unique:estimates,estimate_number', | ||||
|             'user_id' => 'required', | ||||
|             'discount' => 'required', | ||||
|             'discount_val' => 'required', | ||||
| @ -41,10 +40,6 @@ class EstimatesRequest extends FormRequest | ||||
|             'items.*.price' => 'required' | ||||
|         ]; | ||||
|  | ||||
|         if ($this->getMethod() == 'PUT') { | ||||
|             $rules['estimate_number'] = $rules['estimate_number'].','.$this->get('id'); | ||||
|         } | ||||
|  | ||||
|         return $rules; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -25,7 +25,6 @@ class InvoicesRequest extends FormRequest | ||||
|         $rules = [ | ||||
|             'invoice_date' => 'required', | ||||
|             'due_date' => 'required', | ||||
|             'invoice_number' => 'required|unique:invoices,invoice_number', | ||||
|             'user_id' => 'required', | ||||
|             'discount' => 'required', | ||||
|             'discount_val' => 'required', | ||||
| @ -41,10 +40,6 @@ class InvoicesRequest extends FormRequest | ||||
|             'items.*.price' => 'required' | ||||
|         ]; | ||||
|  | ||||
|         if ($this->getMethod() == 'PUT') { | ||||
|             $rules['invoice_number'] = $rules['invoice_number'].','.$this->get('id'); | ||||
|         } | ||||
|  | ||||
|         return $rules; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -24,15 +24,10 @@ class PaymentRequest extends FormRequest | ||||
|     { | ||||
|         $rules = [ | ||||
|             'payment_date' => 'required', | ||||
|             'payment_number' => 'required|unique:payments,payment_number', | ||||
|             'user_id' => 'required', | ||||
|             'amount' => 'required', | ||||
|         ]; | ||||
|  | ||||
|         if ($this->getMethod() == 'PUT') { | ||||
|             $rules['payment_number'] = $rules['payment_number'].','.$this->route('payment'); | ||||
|         } | ||||
|  | ||||
|         return $rules; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -30,7 +30,7 @@ class ProfileRequest extends FormRequest | ||||
|             case 'POST': | ||||
|                 return [ | ||||
|                     'name' => 'required', | ||||
|                     'password' => 'required', | ||||
|                     'password' => 'required|min:8', | ||||
|                     'address_street_1' => 'max:255', | ||||
|                     'address_street_2' => 'max:255', | ||||
|                     'email' => [ | ||||
|  | ||||
| @ -66,10 +66,14 @@ class Invoice extends Model | ||||
|         'formattedDueDate' | ||||
|     ]; | ||||
|  | ||||
|     public static function getNextInvoiceNumber() | ||||
|     public static function getNextInvoiceNumber($value) | ||||
|     { | ||||
|         // Get the last created order | ||||
|         $lastOrder = Invoice::orderBy('created_at', 'desc')->first(); | ||||
|         $lastOrder = Invoice::where('invoice_number', 'LIKE', $value . '-%') | ||||
|                     ->orderBy('created_at', 'desc') | ||||
|                     ->first(); | ||||
|  | ||||
|  | ||||
|         if (!$lastOrder) { | ||||
|             // We get here if there is no order at all | ||||
|             // If there is no number set it to 0, which will be 1 at the end. | ||||
| @ -143,10 +147,15 @@ class Invoice extends Model | ||||
|  | ||||
|     public function getInvoiceNumAttribute() | ||||
|     { | ||||
|         $position = $this->strposX($this->invoice_number, "-", 2) + 1; | ||||
|         $position = $this->strposX($this->invoice_number, "-", 1) + 1; | ||||
|         return substr($this->invoice_number, $position); | ||||
|     } | ||||
|  | ||||
|     public function getInvoicePrefixAttribute () { | ||||
|         $prefix = explode("-", $this->invoice_number)[0]; | ||||
|         return $prefix; | ||||
|     } | ||||
|  | ||||
|     public function getFormattedCreatedAtAttribute($value) | ||||
|     { | ||||
|         $dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id); | ||||
|  | ||||
| @ -6,7 +6,6 @@ use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| use Illuminate\Queue\InteractsWithQueue; | ||||
| use Illuminate\Database\Schema\Blueprint; | ||||
| use Crater\Listeners\Updates\Listener; | ||||
| use Crater\Listeners\Updates\v2\Version200; | ||||
| use Crater\Events\UpdateFinished; | ||||
| use Crater\Setting; | ||||
| use Crater\Address; | ||||
|  | ||||
							
								
								
									
										40
									
								
								app/Listeners/Updates/v2/Version202.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								app/Listeners/Updates/v2/Version202.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Listeners\Updates\v2; | ||||
|  | ||||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| use Illuminate\Queue\InteractsWithQueue; | ||||
| use Crater\Events\UpdateFinished; | ||||
| use Crater\Listeners\Updates\Listener; | ||||
| use Crater\Setting; | ||||
|  | ||||
| class Version202 extends Listener | ||||
| { | ||||
|     const VERSION = '2.0.2'; | ||||
|  | ||||
|     /** | ||||
|      * Create the event listener. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handle the event. | ||||
|      * | ||||
|      * @param  object  $event | ||||
|      * @return void | ||||
|      */ | ||||
|     public function handle(UpdateFinished $event) | ||||
|     { | ||||
|         if ($this->isListenerFired($event)) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Update Crater app version | ||||
|         Setting::setSetting('version', static::VERSION); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										64
									
								
								app/Listeners/Updates/v2/Version210.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								app/Listeners/Updates/v2/Version210.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Crater\Listeners\Updates\v2; | ||||
|  | ||||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| use Illuminate\Queue\InteractsWithQueue; | ||||
| use Crater\Events\UpdateFinished; | ||||
| use Crater\Listeners\Updates\Listener; | ||||
| use Crater\Setting; | ||||
| use Crater\CompanySetting; | ||||
|  | ||||
| class Version210 extends Listener | ||||
| { | ||||
|     const VERSION = '2.1.0'; | ||||
|  | ||||
|     /** | ||||
|      * Create the event listener. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handle the event. | ||||
|      * | ||||
|      * @param  object  $event | ||||
|      * @return void | ||||
|      */ | ||||
|     public function handle(UpdateFinished $event) | ||||
|     { | ||||
|         if ($this->isListenerFired($event)) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Add initial auto generate value | ||||
|         $this->addAutoGenerateSettings(); | ||||
|  | ||||
|         // Update Crater app version | ||||
|         Setting::setSetting('version', static::VERSION); | ||||
|     } | ||||
|  | ||||
|     private function addAutoGenerateSettings() | ||||
|     { | ||||
|         $settings = [ | ||||
|             'invoice_auto_generate' => 'YES', | ||||
|             'invoice_prefix' => 'INV', | ||||
|             'estimate_prefix' => 'EST', | ||||
|             'estimate_auto_generate' => 'YES', | ||||
|             'payment_prefix' => 'PAY', | ||||
|             'payment_auto_generate' => 'YES' | ||||
|         ]; | ||||
|  | ||||
|         foreach ($settings as $key => $value) { | ||||
|             CompanySetting::setSetting( | ||||
|                 $key, | ||||
|                 $value, | ||||
|                 auth()->user()->company->id | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -32,10 +32,34 @@ class Payment extends Model | ||||
|         'formattedPaymentDate' | ||||
|     ]; | ||||
|  | ||||
|     public static function getNextPaymentNumber() | ||||
|  | ||||
|     private function strposX($haystack, $needle, $number) | ||||
|     { | ||||
|         if ($number == '1') { | ||||
|             return strpos($haystack, $needle); | ||||
|         } elseif ($number > '1') { | ||||
|             return strpos( | ||||
|                 $haystack, | ||||
|                 $needle, | ||||
|                 $this->strposX($haystack, $needle, $number - 1) + strlen($needle) | ||||
|             ); | ||||
|         } else { | ||||
|             return error_log('Error: Value for parameter $number is out of range'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function getPaymentNumAttribute() | ||||
|     { | ||||
|         $position = $this->strposX($this->payment_number, "-", 1) + 1; | ||||
|         return substr($this->payment_number, $position); | ||||
|     } | ||||
|  | ||||
|     public static function getNextPaymentNumber($value) | ||||
|     { | ||||
|         // Get the last created order | ||||
|         $payment = Payment::orderBy('created_at', 'desc')->first(); | ||||
|         $payment = Payment::where('payment_number', 'LIKE', $value . '-%') | ||||
|                     ->orderBy('created_at', 'desc') | ||||
|                     ->first(); | ||||
|         if (!$payment) { | ||||
|             // We get here if there is no order at all | ||||
|             // If there is no number set it to 0, which will be 1 at the end. | ||||
| @ -54,6 +78,13 @@ class Payment extends Model | ||||
|         return sprintf('%06d', intval($number) + 1); | ||||
|     } | ||||
|  | ||||
|     public function getPaymentPrefixAttribute () | ||||
|     { | ||||
|         $prefix= explode("-",$this->payment_number)[0]; | ||||
|         return $prefix; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public function invoice() | ||||
|     { | ||||
|         return $this->belongsTo(Invoice::class); | ||||
|  | ||||
| @ -8,6 +8,8 @@ use Crater\Events\UpdateFinished; | ||||
| use Crater\Listeners\Updates\v1\Version110; | ||||
| use Crater\Listeners\Updates\v2\Version200; | ||||
| use Crater\Listeners\Updates\v2\Version201; | ||||
| use Crater\Listeners\Updates\v2\Version202; | ||||
| use Crater\Listeners\Updates\v2\Version210; | ||||
|  | ||||
| class EventServiceProvider extends ServiceProvider | ||||
| { | ||||
| @ -21,6 +23,8 @@ class EventServiceProvider extends ServiceProvider | ||||
|             Version110::class, | ||||
|             Version200::class, | ||||
|             Version201::class, | ||||
|             Version202::class, | ||||
|             Version210::class, | ||||
|         ], | ||||
|         Registered::class => [ | ||||
|             SendEmailVerificationNotification::class, | ||||
|  | ||||
| @ -61,7 +61,7 @@ class EnvironmentManager | ||||
|         } catch (Exception $e) { | ||||
|  | ||||
|             return [ | ||||
|                 'error' => $e->getMessage() | ||||
|                 'error_message' => $e->getMessage() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|  | ||||
| @ -41,10 +41,14 @@ function clean_slug($string) | ||||
|  * @param $money | ||||
|  * @return formated_money | ||||
|  */ | ||||
| function format_money_pdf($money) | ||||
| function format_money_pdf($money, $currency = null) | ||||
| { | ||||
|     $money = $money / 100; | ||||
|  | ||||
|     if (!$currency) { | ||||
|         $currency = Currency::findOrFail(CompanySetting::getSetting('currency', 1)); | ||||
|     } | ||||
|  | ||||
|     $format_money = number_format( | ||||
|         $money, | ||||
|         $currency->precision, | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
|  <?php | ||||
| <?php | ||||
|  | ||||
| return [ | ||||
|  | ||||
|  | ||||
| @ -9,6 +9,6 @@ return [ | ||||
|     | | ||||
|     */ | ||||
|  | ||||
|     'version' => '2.0.1', | ||||
|     'version' => '2.1.0', | ||||
|  | ||||
| ]; | ||||
|  | ||||
							
								
								
									
										244
									
								
								config/dompdf.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								config/dompdf.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,244 @@ | ||||
| <?php | ||||
|  | ||||
| return array( | ||||
|  | ||||
|     /* | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | Settings | ||||
|     |-------------------------------------------------------------------------- | ||||
|     | | ||||
|     | Set some default values. It is possible to add all defines that can be set | ||||
|     | in dompdf_config.inc.php. You can also override the entire config file. | ||||
|     | | ||||
|     */ | ||||
|     'show_warnings' => false,   // Throw an Exception on warnings from dompdf | ||||
|     'orientation' => 'portrait', | ||||
|     'defines' => array( | ||||
|         /** | ||||
|          * The location of the DOMPDF font directory | ||||
|          * | ||||
|          * The location of the directory where DOMPDF will store fonts and font metrics | ||||
|          * Note: This directory must exist and be writable by the webserver process. | ||||
|          * *Please note the trailing slash.* | ||||
|          * | ||||
|          * Notes regarding fonts: | ||||
|          * Additional .afm font metrics can be added by executing load_font.php from command line. | ||||
|          * | ||||
|          * Only the original "Base 14 fonts" are present on all pdf viewers. Additional fonts must | ||||
|          * be embedded in the pdf file or the PDF may not display correctly. This can significantly | ||||
|          * increase file size unless font subsetting is enabled. Before embedding a font please | ||||
|          * review your rights under the font license. | ||||
|          * | ||||
|          * Any font specification in the source HTML is translated to the closest font available | ||||
|          * in the font directory. | ||||
|          * | ||||
|          * The pdf standard "Base 14 fonts" are: | ||||
|          * Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique, | ||||
|          * Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique, | ||||
|          * Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic, | ||||
|          * Symbol, ZapfDingbats. | ||||
|          */ | ||||
|         "font_dir" => storage_path('fonts/'), // advised by dompdf (https://github.com/dompdf/dompdf/pull/782) | ||||
|  | ||||
|         /** | ||||
|          * The location of the DOMPDF font cache directory | ||||
|          * | ||||
|          * This directory contains the cached font metrics for the fonts used by DOMPDF. | ||||
|          * This directory can be the same as DOMPDF_FONT_DIR | ||||
|          * | ||||
|          * Note: This directory must exist and be writable by the webserver process. | ||||
|          */ | ||||
|         "font_cache" => storage_path('fonts/'), | ||||
|  | ||||
|         /** | ||||
|          * The location of a temporary directory. | ||||
|          * | ||||
|          * The directory specified must be writeable by the webserver process. | ||||
|          * The temporary directory is required to download remote images and when | ||||
|          * using the PFDLib back end. | ||||
|          */ | ||||
|         "temp_dir" => sys_get_temp_dir(), | ||||
|  | ||||
|         /** | ||||
|          * ==== IMPORTANT ==== | ||||
|          * | ||||
|          * dompdf's "chroot": Prevents dompdf from accessing system files or other | ||||
|          * files on the webserver.  All local files opened by dompdf must be in a | ||||
|          * subdirectory of this directory.  DO NOT set it to '/' since this could | ||||
|          * allow an attacker to use dompdf to read any files on the server.  This | ||||
|          * should be an absolute path. | ||||
|          * This is only checked on command line call by dompdf.php, but not by | ||||
|          * direct class use like: | ||||
|          * $dompdf = new DOMPDF();	$dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output(); | ||||
|          */ | ||||
|         "chroot" => realpath(base_path()), | ||||
|  | ||||
|         /** | ||||
|          * Whether to enable font subsetting or not. | ||||
|          */ | ||||
|         "enable_font_subsetting" => false, | ||||
|  | ||||
|         /** | ||||
|          * The PDF rendering backend to use | ||||
|          * | ||||
|          * Valid settings are 'PDFLib', 'CPDF' (the bundled R&OS PDF class), 'GD' and | ||||
|          * 'auto'. 'auto' will look for PDFLib and use it if found, or if not it will | ||||
|          * fall back on CPDF. 'GD' renders PDFs to graphic files. {@link | ||||
|          * Canvas_Factory} ultimately determines which rendering class to instantiate | ||||
|          * based on this setting. | ||||
|          * | ||||
|          * Both PDFLib & CPDF rendering backends provide sufficient rendering | ||||
|          * capabilities for dompdf, however additional features (e.g. object, | ||||
|          * image and font support, etc.) differ between backends.  Please see | ||||
|          * {@link PDFLib_Adapter} for more information on the PDFLib backend | ||||
|          * and {@link CPDF_Adapter} and lib/class.pdf.php for more information | ||||
|          * on CPDF. Also see the documentation for each backend at the links | ||||
|          * below. | ||||
|          * | ||||
|          * The GD rendering backend is a little different than PDFLib and | ||||
|          * CPDF. Several features of CPDF and PDFLib are not supported or do | ||||
|          * not make any sense when creating image files.  For example, | ||||
|          * multiple pages are not supported, nor are PDF 'objects'.  Have a | ||||
|          * look at {@link GD_Adapter} for more information.  GD support is | ||||
|          * experimental, so use it at your own risk. | ||||
|          * | ||||
|          * @link http://www.pdflib.com | ||||
|          * @link http://www.ros.co.nz/pdf | ||||
|          * @link http://www.php.net/image | ||||
|          */ | ||||
|         "pdf_backend" => "CPDF", | ||||
|  | ||||
|         /** | ||||
|          * PDFlib license key | ||||
|          * | ||||
|          * If you are using a licensed, commercial version of PDFlib, specify | ||||
|          * your license key here.  If you are using PDFlib-Lite or are evaluating | ||||
|          * the commercial version of PDFlib, comment out this setting. | ||||
|          * | ||||
|          * @link http://www.pdflib.com | ||||
|          * | ||||
|          * If pdflib present in web server and auto or selected explicitely above, | ||||
|          * a real license code must exist! | ||||
|          */ | ||||
|         //"DOMPDF_PDFLIB_LICENSE" => "your license key here", | ||||
|  | ||||
|         /** | ||||
|          * html target media view which should be rendered into pdf. | ||||
|          * List of types and parsing rules for future extensions: | ||||
|          * http://www.w3.org/TR/REC-html40/types.html | ||||
|          *   screen, tty, tv, projection, handheld, print, braille, aural, all | ||||
|          * Note: aural is deprecated in CSS 2.1 because it is replaced by speech in CSS 3. | ||||
|          * Note, even though the generated pdf file is intended for print output, | ||||
|          * the desired content might be different (e.g. screen or projection view of html file). | ||||
|          * Therefore allow specification of content here. | ||||
|          */ | ||||
|         "default_media_type" => "screen", | ||||
|  | ||||
|         /** | ||||
|          * The default paper size. | ||||
|          * | ||||
|          * North America standard is "letter"; other countries generally "a4" | ||||
|          * | ||||
|          * @see CPDF_Adapter::PAPER_SIZES for valid sizes ('letter', 'legal', 'A4', etc.) | ||||
|          */ | ||||
|         "default_paper_size" => "a4", | ||||
|  | ||||
|         /** | ||||
|          * The default font family | ||||
|          * | ||||
|          * Used if no suitable fonts can be found. This must exist in the font folder. | ||||
|          * @var string | ||||
|          */ | ||||
|         "default_font" => "DejaVu Sans", | ||||
|  | ||||
|         /** | ||||
|          * Image DPI setting | ||||
|          * | ||||
|          * This setting determines the default DPI setting for images and fonts.  The | ||||
|          * DPI may be overridden for inline images by explictly setting the | ||||
|          * image's width & height style attributes (i.e. if the image's native | ||||
|          * width is 600 pixels and you specify the image's width as 72 points, | ||||
|          * the image will have a DPI of 600 in the rendered PDF.  The DPI of | ||||
|          * background images can not be overridden and is controlled entirely | ||||
|          * via this parameter. | ||||
|          * | ||||
|          * For the purposes of DOMPDF, pixels per inch (PPI) = dots per inch (DPI). | ||||
|          * If a size in html is given as px (or without unit as image size), | ||||
|          * this tells the corresponding size in pt. | ||||
|          * This adjusts the relative sizes to be similar to the rendering of the | ||||
|          * html page in a reference browser. | ||||
|          * | ||||
|          * In pdf, always 1 pt = 1/72 inch | ||||
|          * | ||||
|          * Rendering resolution of various browsers in px per inch: | ||||
|          * Windows Firefox and Internet Explorer: | ||||
|          *   SystemControl->Display properties->FontResolution: Default:96, largefonts:120, custom:? | ||||
|          * Linux Firefox: | ||||
|          *   about:config *resolution: Default:96 | ||||
|          *   (xorg screen dimension in mm and Desktop font dpi settings are ignored) | ||||
|          * | ||||
|          * Take care about extra font/image zoom factor of browser. | ||||
|          * | ||||
|          * In images, <img> size in pixel attribute, img css style, are overriding | ||||
|          * the real image dimension in px for rendering. | ||||
|          * | ||||
|          * @var int | ||||
|          */ | ||||
|         "dpi" => 96, | ||||
|  | ||||
|         /** | ||||
|          * Enable inline PHP | ||||
|          * | ||||
|          * If this setting is set to true then DOMPDF will automatically evaluate | ||||
|          * inline PHP contained within <script type="text/php"> ... </script> tags. | ||||
|          * | ||||
|          * Enabling this for documents you do not trust (e.g. arbitrary remote html | ||||
|          * pages) is a security risk.  Set this option to false if you wish to process | ||||
|          * untrusted documents. | ||||
|          * | ||||
|          * @var bool | ||||
|          */ | ||||
|         "enable_php" => false, | ||||
|  | ||||
|         /** | ||||
|          * Enable inline Javascript | ||||
|          * | ||||
|          * If this setting is set to true then DOMPDF will automatically insert | ||||
|          * JavaScript code contained within <script type="text/javascript"> ... </script> tags. | ||||
|          * | ||||
|          * @var bool | ||||
|          */ | ||||
|         "enable_javascript" => true, | ||||
|  | ||||
|         /** | ||||
|          * Enable remote file access | ||||
|          * | ||||
|          * If this setting is set to true, DOMPDF will access remote sites for | ||||
|          * images and CSS files as required. | ||||
|          * This is required for part of test case www/test/image_variants.html through www/examples.php | ||||
|          * | ||||
|          * Attention! | ||||
|          * This can be a security risk, in particular in combination with DOMPDF_ENABLE_PHP and | ||||
|          * allowing remote access to dompdf.php or on allowing remote html code to be passed to | ||||
|          * $dompdf = new DOMPDF(, $dompdf->load_html(..., | ||||
|          * This allows anonymous users to download legally doubtful internet content which on | ||||
|          * tracing back appears to being downloaded by your server, or allows malicious php code | ||||
|          * in remote html pages to be executed by your server with your account privileges. | ||||
|          * | ||||
|          * @var bool | ||||
|          */ | ||||
|         "enable_remote" => true, | ||||
|  | ||||
|         /** | ||||
|          * A ratio applied to the fonts height to be more like browsers' line height | ||||
|          */ | ||||
|         "font_height_ratio" => 1.1, | ||||
|  | ||||
|         /** | ||||
|          * Use the more-than-experimental HTML5 Lib parser | ||||
|          */ | ||||
|         "enable_html5_parser" => true, | ||||
|     ), | ||||
|  | ||||
|  | ||||
| ); | ||||
| @ -20,9 +20,9 @@ class CreateMediaTable extends Migration | ||||
|             $table->string('mime_type')->nullable(); | ||||
|             $table->string('disk'); | ||||
|             $table->unsignedInteger('size'); | ||||
|             $table->json('manipulations'); | ||||
|             $table->json('custom_properties'); | ||||
|             $table->json('responsive_images'); | ||||
|             $table->text('manipulations'); | ||||
|             $table->text('custom_properties'); | ||||
|             $table->text('responsive_images'); | ||||
|             $table->unsignedInteger('order_column')->nullable(); | ||||
|             $table->nullableTimestamps(); | ||||
|         }); | ||||
|  | ||||
							
								
								
									
										40
									
								
								docker-compose.yaml.example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								docker-compose.yaml.example
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| version: '3.1' | ||||
|  | ||||
| services: | ||||
|  | ||||
|   web: | ||||
|     image: nginx | ||||
|     depends_on: | ||||
|       - php | ||||
|     ports: | ||||
|       - 8080:80 | ||||
|     volumes: | ||||
|       - ./nginx.conf:/etc/nginx/nginx.conf:ro | ||||
|       - app:/app | ||||
|     restart: always  | ||||
|      | ||||
|   php: | ||||
|     build: . | ||||
|     depends_on: | ||||
|       - db | ||||
|     expose: | ||||
|       - 9000 | ||||
|     volumes: | ||||
|       - app:/app | ||||
|     restart: always | ||||
|  | ||||
|   db: | ||||
|     image: mariadb | ||||
|     restart: always | ||||
|     volumes: | ||||
|       - db:/var/lib/mysql | ||||
|     environment: | ||||
|       MYSQL_USER: crater | ||||
|       MYSQL_PASSWORD: crater | ||||
|       MYSQL_DATABASE: crater | ||||
|       MYSQL_ROOT_PASSWORD: crater | ||||
|  | ||||
| volumes: | ||||
|   app: | ||||
|   db: | ||||
|  | ||||
							
								
								
									
										53
									
								
								nginx.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								nginx.conf
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| worker_processes  8; | ||||
|  | ||||
| error_log  /var/log/nginx/error.log warn; | ||||
| pid        /var/run/nginx.pid; | ||||
|  | ||||
| events { | ||||
|     worker_connections  4096; | ||||
| } | ||||
|  | ||||
| http { | ||||
|     include       /etc/nginx/mime.types; | ||||
|     default_type  application/octet-stream; | ||||
|  | ||||
|     log_format  main  '$remote_addr - $remote_user [$time_local] "$request" ' | ||||
|                       '$status $body_bytes_sent "$http_referer" ' | ||||
|                       '"$http_user_agent" "$http_x_forwarded_for"'; | ||||
|  | ||||
|     access_log  /var/log/nginx/access.log  main; | ||||
|  | ||||
|     sendfile        on; | ||||
|  | ||||
|     keepalive_timeout  65; | ||||
|  | ||||
|     server { | ||||
|         listen 80 default_server; | ||||
|  | ||||
|         root /app/public; | ||||
|         index index.php; | ||||
|         charset utf-8; | ||||
|  | ||||
|         access_log off; | ||||
|  | ||||
|         location / { | ||||
|             try_files $uri $uri/ /index.php?$query_string; | ||||
|         } | ||||
|  | ||||
|         location = /favicon.ico { access_log off; log_not_found off; } | ||||
|         location = /robots.txt  { access_log off; log_not_found off; } | ||||
|  | ||||
|         add_header X-Content-Type-Options nosniff; | ||||
|         add_header X-XSS-Protection "1; mode=block"; | ||||
|         add_header X-Robots-Tag none; | ||||
|         add_header Content-Security-Policy "frame-ancestors 'self'"; | ||||
|  | ||||
|         location ~ \.php$ { | ||||
|             fastcgi_pass php:9000; | ||||
|             fastcgi_index index.php; | ||||
|             include fastcgi_params; | ||||
|             fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; | ||||
|             include /etc/nginx/fastcgi_params; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										2
									
								
								public/assets/css/crater.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/assets/css/crater.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								public/assets/css/crater.css.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								public/assets/css/crater.css.map
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -1,4 +1,4 @@ | ||||
| { | ||||
|     "/assets/js/app.js": "/assets/js/app.js?id=2a5cfd8271e10bd501dc", | ||||
|     "/assets/css/crater.css": "/assets/css/crater.css?id=108e3a8d009e7d38018c" | ||||
|     "/assets/js/app.js": "/assets/js/app.js?id=a9f802b3fe774e87bf0c", | ||||
|     "/assets/css/crater.css": "/assets/css/crater.css?id=193e5770a0e7a8604f35" | ||||
| } | ||||
|  | ||||
| @ -29,7 +29,7 @@ Web Application is made using Laravel & VueJS while the Mobile Apps are built us | ||||
|  | ||||
| ## Mobile Apps | ||||
| - [Android](https://play.google.com/store/apps/details?id=com.craterapp.app) | ||||
| - IOS - Coming Soon | ||||
| - [IOS](https://apps.apple.com/app/id1489169767) | ||||
| - [Source](https://github.com/bytefury/crater-mobile) | ||||
|  | ||||
| ## Discord | ||||
| @ -60,6 +60,10 @@ Join the Crater discord server to discuss: | ||||
| ## Credits | ||||
| Crater is a product of [Bytefury](https://bytefury.com) | ||||
|  | ||||
| **Special thanks to:** | ||||
| * [Birkhoff Lee](https://github.com/BirkhoffLee) | ||||
|  | ||||
|  | ||||
| ## Translate | ||||
| Help us translate on [Transifex](https://www.transifex.com/bytefury/crater-invoice) | ||||
|  | ||||
|  | ||||
							
								
								
									
										71
									
								
								resources/assets/js/components/base/BasePrefixInput.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								resources/assets/js/components/base/BasePrefixInput.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| <template> | ||||
|   <div class="base-prefix-input" @click="focusInput"> | ||||
|     <font-awesome-icon v-if="icon" :icon="icon" class="icon" /> | ||||
|     <p class="prefix-label"><span class="mr-1">{{ prefix }}</span>-</p> | ||||
|     <input | ||||
|       ref="basePrefixInput" | ||||
|       v-model="inputValue" | ||||
|       :type="type" | ||||
|       class="prefix-input-field" | ||||
|       @input="handleInput" | ||||
|       @change="handleChange" | ||||
|       @keyup="handleKeyupEnter" | ||||
|       @keydown="handleKeyDownEnter" | ||||
|       @blur="handleFocusOut" | ||||
|     > | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   props: { | ||||
|     prefix: { | ||||
|       type: String, | ||||
|       default: null, | ||||
|       required: true | ||||
|     }, | ||||
|     icon: { | ||||
|       type: String, | ||||
|       default: null | ||||
|     }, | ||||
|     value: { | ||||
|       type: [String, Number, File], | ||||
|       default: '' | ||||
|     }, | ||||
|     type: { | ||||
|       type: String, | ||||
|       default: 'text' | ||||
|     } | ||||
|   }, | ||||
|   data () { | ||||
|     return { | ||||
|       inputValue: this.value | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     'value' () { | ||||
|       this.inputValue = this.value | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     focusInput () { | ||||
|       this.$refs.basePrefixInput.focus() | ||||
|     }, | ||||
|     handleInput (e) { | ||||
|       this.$emit('input', this.inputValue) | ||||
|     }, | ||||
|     handleChange (e) { | ||||
|       this.$emit('change', this.inputValue) | ||||
|     }, | ||||
|     handleKeyupEnter (e) { | ||||
|       this.$emit('keyup', this.inputValue) | ||||
|     }, | ||||
|     handleKeyDownEnter (e) { | ||||
|       this.$emit('keydown', e, this.inputValue) | ||||
|     }, | ||||
|     handleFocusOut (e) { | ||||
|       this.$emit('blur', this.inputValue) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| @ -8,6 +8,7 @@ import BaseTextArea from './BaseTextArea.vue' | ||||
| import BaseSelect from './base-select/BaseSelect.vue' | ||||
| import BaseLoader from './BaseLoader.vue' | ||||
| import BaseCustomerSelect from './BaseCustomerSelect.vue' | ||||
| import BasePrefixInput from './BasePrefixInput.vue' | ||||
|  | ||||
| import BasePopup from './popup/BasePopup.vue' | ||||
| import CustomerSelectPopup from './popup/CustomerSelectPopup.vue' | ||||
| @ -23,6 +24,7 @@ Vue.component('base-input', BaseInput) | ||||
| Vue.component('base-switch', BaseSwitch) | ||||
| Vue.component('base-text-area', BaseTextArea) | ||||
| Vue.component('base-loader', BaseLoader) | ||||
| Vue.component('base-prefix-input', BasePrefixInput) | ||||
|  | ||||
| Vue.component('table-component', TableComponent) | ||||
| Vue.component('table-column', TableColumn) | ||||
|  | ||||
| @ -55,6 +55,7 @@ | ||||
|                     v-model="currency" | ||||
|                     :options="currencies" | ||||
|                     :searchable="true" | ||||
|                     :allow-empty="false" | ||||
|                     :show-labels="false" | ||||
|                     :placeholder="$t('customers.select_currency')" | ||||
|                     label="name" | ||||
|  | ||||
| @ -124,7 +124,7 @@ export default { | ||||
|       }, | ||||
|       percent: { | ||||
|         required, | ||||
|         between: between(0.10, 100) | ||||
|         between: between(0, 100) | ||||
|       }, | ||||
|       description: { | ||||
|         maxLength: maxLength(255) | ||||
|  | ||||
| @ -530,6 +530,7 @@ | ||||
|     "menu_title": { | ||||
|       "account_settings": "Account Settings", | ||||
|       "company_information": "Company Information", | ||||
|       "customization": "Customization", | ||||
|       "preferences": "Preferences", | ||||
|       "notifications": "Notifications", | ||||
|       "tax_types": "Tax Types", | ||||
| @ -598,6 +599,67 @@ | ||||
|       "save": "Save", | ||||
|       "updated_message": "Company information updated successfully" | ||||
|     }, | ||||
|     "customization": { | ||||
|         "customization": "customization", | ||||
|         "save": "Save", | ||||
|         "addresses": { | ||||
|             "title": "Addresses", | ||||
|             "section_description": "You can set Customer Billing Address and Customer Shipping Address Format (Displayed in PDF only). ", | ||||
|             "customer_billing_address": "Customer Billing Address", | ||||
|             "customer_shipping_address": "Customer Shipping Address", | ||||
|             "company_address": "Company Address", | ||||
|             "insert_fields": "Insert Fields", | ||||
|             "contact": "Contact", | ||||
|             "address": "Address", | ||||
|             "display_name": "Display Name", | ||||
|             "primary_contact_name": "Primary Contact Name", | ||||
|             "email": "Email", | ||||
|             "website": "Website", | ||||
|             "name": "Name", | ||||
|             "country": "Country", | ||||
|             "state": "State", | ||||
|             "city": "City", | ||||
|             "company_name": "Company Name", | ||||
|             "address_street_1": "Address Street 1", | ||||
|             "address_street_2": "Address Street 2", | ||||
|             "phone": "Phone", | ||||
|             "zip_code": "Zip Code", | ||||
|             "address_setting_updated": "Address Setting updated successfully" | ||||
|         }, | ||||
|         "updated_message": "Company information updated successfully", | ||||
|  | ||||
|         "invoices": { | ||||
|             "title": "Invoices", | ||||
|             "notes": "Notes", | ||||
|             "invoice_prefix": "Invoice Prefix", | ||||
|             "invoice_settings": "Invoice Settings", | ||||
|             "autogenerate_invoice_number": "Autogenerate Invoice Number", | ||||
|             "invoice_setting_description": "Disable this, If you don't wish to auto-generate invoice numbers each time you create a new invoice.", | ||||
|             "enter_invoice_prefix": "Enter invoice prefix", | ||||
|             "terms_and_conditions": "Terms and Conditions", | ||||
|             "invoice_setting_updated": "Invoice Setting updated successfully" | ||||
|         }, | ||||
|  | ||||
|         "estimates": { | ||||
|             "title": "Estimates", | ||||
|             "estimate_prefix": "Estimate Prefix", | ||||
|             "estimate_settings": "Estimate Settings", | ||||
|             "autogenerate_estimate_number": "Autogenerate Estimate Number", | ||||
|             "estimate_setting_description": "Disable this, If you don't wish to auto-generate estimate numbers each time you create a new estimate.", | ||||
|             "enter_estimate_prefix": "Enter estmiate prefix", | ||||
|             "estimate_setting_updated": "Estimate Setting updated successfully" | ||||
|         }, | ||||
|  | ||||
|         "payments": { | ||||
|             "title": "Payments", | ||||
|             "payment_prefix": "Payment Prefix", | ||||
|             "payment_settings": "Payment Settings", | ||||
|             "autogenerate_payment_number": "Autogenerate Payment Number", | ||||
|             "payment_setting_description": "Disable this, If you don't wish to auto-generate payment numbers each time you create a new payment.", | ||||
|             "enter_payment_prefix": "Enter Payment Prefix", | ||||
|             "payment_setting_updated": "Payment Setting updated successfully" | ||||
|         } | ||||
|     }, | ||||
|     "account_settings": { | ||||
|       "profile_picture": "Profile Picture", | ||||
|       "name": "Name", | ||||
| @ -666,7 +728,7 @@ | ||||
|       "date_format": "Date Format", | ||||
|       "discount_setting": "Discount Setting", | ||||
|       "discount_per_item": "Discount Per Item ", | ||||
|       "discount_setting_description": "Enable this if you want to add Discount to individual invoice items. By default, Discount are added directly to the invoice.", | ||||
|       "discount_setting_description": "Enable this if you want to add Discount to individual invoice items. By default, Discount is added directly to the invoice.", | ||||
|       "save": "Save", | ||||
|       "preference": "Preference | Preferences", | ||||
|       "general_settings": "Default preferences for the system.", | ||||
| @ -812,6 +874,7 @@ | ||||
|     "maximum_options_error": "Maximum  of {max} options selected. First remove a selected option to select another.", | ||||
|     "notes_maxlength": "Notes should not be greater than 255 characters.", | ||||
|     "address_maxlength": "Address should not be greater than 255 characters.", | ||||
|     "ref_number_maxlength": "Ref Number should not be greater than 255 characters." | ||||
|     "ref_number_maxlength": "Ref Number should not be greater than 255 characters.", | ||||
|     "prefix_maxlength": "Prefix should not be greater than 5 characters." | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -66,6 +66,7 @@ import ReportLayout from './views/reports/layout/Index.vue' | ||||
| // Settings | ||||
| import SettingsLayout from './views/settings/layout/Index.vue' | ||||
| import CompanyInfo from './views/settings/CompanyInfo.vue' | ||||
| import Customization from './views/settings/Customization.vue' | ||||
| import Notifications from './views/settings/Notifications.vue' | ||||
| import Preferences from './views/settings/Preferences.vue' | ||||
| import UserProfile from './views/settings/UserProfile.vue' | ||||
| @ -309,6 +310,11 @@ const routes = [ | ||||
|             name: 'company.info', | ||||
|             component: CompanyInfo | ||||
|           }, | ||||
|           { | ||||
|             path: 'customization', | ||||
|             name: 'customization', | ||||
|             component: Customization | ||||
|           }, | ||||
|           { | ||||
|             path: 'user-profile', | ||||
|             name: 'user.profile', | ||||
|  | ||||
| @ -9,10 +9,11 @@ export default { | ||||
|   bootstrap ({ commit, dispatch, state }) { | ||||
|     return new Promise((resolve, reject) => { | ||||
|       window.axios.get('/api/bootstrap').then((response) => { | ||||
|         commit('company/' + companyTypes.BOOTSTRAP_COMPANIES, response.data.companies) | ||||
|         commit('company/' + companyTypes.SET_SELECTED_COMPANY, response.data.company) | ||||
|         commit('currency/' + currencyTypes.BOOTSTRAP_CURRENCIES, response.data) | ||||
|         commit('currency/' + currencyTypes.SET_DEFAULT_CURRENCY, response.data) | ||||
|         commit('user/' + userTypes.BOOTSTRAP_CURRENT_USER, response.data.user) | ||||
|         commit('company/' + companyTypes.BOOTSTRAP_COMPANIES, response.data.companies) | ||||
|         commit('taxType/' + taxTypeTypes.BOOTSTRAP_TAX_TYPES, response.data.taxTypes) | ||||
|         commit('preferences/' + preferencesTypes.SET_MOMENT_DATE_FORMAT, response.data.moment_date_format) | ||||
|         commit('preferences/' + preferencesTypes.SET_LANGUAGE_FORMAT, response.data.default_language) | ||||
|  | ||||
| @ -1,7 +1,5 @@ | ||||
| import * as types from './mutation-types' | ||||
| import Ls from '@/services/ls' | ||||
|  | ||||
| export const setSelectedCompany = ({ commit, dispatch, state }, data) => { | ||||
|   Ls.set('selectedCompany', data.id) | ||||
|   commit(types.SET_SELECTED_COMPANY, data) | ||||
| } | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import * as types from './mutation-types' | ||||
| import Ls from '@/services/ls' | ||||
|  | ||||
| export default { | ||||
|   [types.BOOTSTRAP_COMPANIES] (state, companies) { | ||||
| @ -6,6 +7,7 @@ export default { | ||||
|     state.selectedCompany = companies[0] | ||||
|   }, | ||||
|   [types.SET_SELECTED_COMPANY] (state, company) { | ||||
|     Ls.set('selectedCompany', company.id) | ||||
|     state.selectedCompany = company | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -61,6 +61,7 @@ | ||||
|                 <base-select | ||||
|                   v-model="currency" | ||||
|                   :options="currencies" | ||||
|                   :custom-label="currencyNameWithCode" | ||||
|                   :allow-empty="false" | ||||
|                   :searchable="true" | ||||
|                   :show-labels="false" | ||||
| @ -161,7 +162,7 @@ | ||||
|                   :options="billingCountries" | ||||
|                   :searchable="true" | ||||
|                   :show-labels="false" | ||||
|                   :allow-empty="false" | ||||
|                   :allow-empty="true" | ||||
|                   :tabindex="8" | ||||
|                   :placeholder="$t('general.select_country')" | ||||
|                   label="name" | ||||
| @ -264,7 +265,7 @@ | ||||
|                   :searchable="true" | ||||
|                   :show-labels="false" | ||||
|                   :tabindex="16" | ||||
|                   :allow-empty="false" | ||||
|                   :allow-empty="true" | ||||
|                   :placeholder="$t('general.select_country')" | ||||
|                   label="name" | ||||
|                   track-by="id" | ||||
| @ -410,6 +411,36 @@ export default { | ||||
|         return true | ||||
|       } | ||||
|       return false | ||||
|     }, | ||||
|     hasBillingAdd () { | ||||
|       let billing = this.billing | ||||
|       if ( | ||||
|         billing.name || | ||||
|         billing.country_id || | ||||
|         billing.state || | ||||
|         billing.city || | ||||
|         billing.phone || | ||||
|         billing.zip || | ||||
|         billing.address_street_1 || | ||||
|         billing.address_street_2) { | ||||
|         return true | ||||
|       } | ||||
|       return false | ||||
|     }, | ||||
|     hasShippingAdd () { | ||||
|       let shipping = this.shipping | ||||
|       if ( | ||||
|         shipping.name || | ||||
|         shipping.country_id || | ||||
|         shipping.state || | ||||
|         shipping.city || | ||||
|         shipping.phone || | ||||
|         shipping.zip || | ||||
|         shipping.address_street_1 || | ||||
|         shipping.address_street_2) { | ||||
|         return true | ||||
|       } | ||||
|       return false | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
| @ -417,12 +448,16 @@ export default { | ||||
|       if (newCountry) { | ||||
|         this.billing.country_id = newCountry.id | ||||
|         this.isDisabledBillingState = false | ||||
|       } else { | ||||
|         this.billing.country_id = null | ||||
|       } | ||||
|     }, | ||||
|     shipping_country (newCountry) { | ||||
|       if (newCountry) { | ||||
|         this.shipping.country_id = newCountry.id | ||||
|         return true | ||||
|       } else { | ||||
|         this.shipping.country_id = null | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
| @ -435,6 +470,9 @@ export default { | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     currencyNameWithCode ({name, code}) { | ||||
|       return `${code} - ${name}` | ||||
|     }, | ||||
|     ...mapActions('customer', [ | ||||
|       'addCustomer', | ||||
|       'fetchCustomer', | ||||
| @ -442,7 +480,14 @@ export default { | ||||
|     ]), | ||||
|     async loadCustomer () { | ||||
|       let { data: { customer, currencies, currency } } = await this.fetchCustomer(this.$route.params.id) | ||||
|       this.formData = customer | ||||
|  | ||||
|       this.formData.id = customer.id | ||||
|       this.formData.name = customer.name | ||||
|       this.formData.contact_name = customer.contact_name | ||||
|       this.formData.email = customer.email | ||||
|       this.formData.phone = customer.phone | ||||
|       this.formData.currency_id = customer.currency_id | ||||
|       this.formData.website = customer.website | ||||
|  | ||||
|       if (customer.billing_address) { | ||||
|         this.billing = customer.billing_address | ||||
| @ -491,7 +536,16 @@ export default { | ||||
|       if (this.$v.$invalid) { | ||||
|         return true | ||||
|       } | ||||
|       if (this.hasBillingAdd && this.hasShippingAdd) { | ||||
|         this.formData.addresses = [{...this.billing}, {...this.shipping}] | ||||
|       } else { | ||||
|         if (this.hasBillingAdd) { | ||||
|           this.formData.addresses = [{...this.billing}] | ||||
|         } | ||||
|         if (this.hasShippingAdd) { | ||||
|           this.formData.addresses = [{...this.shipping}] | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if (this.isEdit) { | ||||
|         if (this.currency) { | ||||
|  | ||||
| @ -32,8 +32,8 @@ | ||||
|             class="show-customer" | ||||
|           > | ||||
|             <div class="row px-2 mt-1"> | ||||
|               <div class="col col-6"> | ||||
|                 <div v-if="selectedCustomer.billing_address != null" class="row address-menu"> | ||||
|               <div v-if="selectedCustomer.billing_address" class="col col-6"> | ||||
|                 <div class="row address-menu"> | ||||
|                   <label class="col-sm-4 px-2 title">{{ $t('general.bill_to') }}</label> | ||||
|                   <div class="col-sm p-0 px-2 content"> | ||||
|                     <label v-if="selectedCustomer.billing_address.name"> | ||||
| @ -57,8 +57,8 @@ | ||||
|                   </div> | ||||
|                 </div> | ||||
|               </div> | ||||
|               <div class="col col-6"> | ||||
|                 <div v-if="selectedCustomer.shipping_address != null" class="row address-menu"> | ||||
|               <div v-if="selectedCustomer.shipping_address" class="col col-6"> | ||||
|                 <div class="row address-menu"> | ||||
|                   <label class="col-sm-4 px-2 title">{{ $t('general.ship_to') }}</label> | ||||
|                   <div class="col-sm p-0 px-2 content"> | ||||
|                     <label v-if="selectedCustomer.shipping_address.name"> | ||||
| @ -84,7 +84,7 @@ | ||||
|               </div> | ||||
|             </div> | ||||
|             <div class="customer-content mb-1"> | ||||
|               <label class="email">{{ selectedCustomer.email ? selectedCustomer.email : selectedCustomer.name }}</label> | ||||
|               <label class="email">{{ selectedCustomer.name }}</label> | ||||
|               <label class="action" @click="removeCustomer">{{ $t('general.remove') }}</label> | ||||
|             </div> | ||||
|           </div> | ||||
| @ -127,14 +127,15 @@ | ||||
|           <div class="row mt-4"> | ||||
|             <div class="col collapse-input"> | ||||
|               <label>{{ $t('estimates.estimate_number') }}<span class="text-danger"> * </span></label> | ||||
|               <base-input | ||||
|                 :invalid="$v.newEstimate.estimate_number.$error" | ||||
|                 :read-only="true" | ||||
|                 v-model="newEstimate.estimate_number" | ||||
|               <base-prefix-input | ||||
|                 v-model="estimateNumAttribute" | ||||
|                 :invalid="$v.estimateNumAttribute.$error" | ||||
|                 :prefix="estimatePrefix" | ||||
|                 icon="hashtag" | ||||
|                 @input="$v.newEstimate.estimate_number.$touch()" | ||||
|                 @input="$v.estimateNumAttribute.$touch()" | ||||
|               /> | ||||
|               <span v-show="$v.newEstimate.estimate_number.$error && !$v.newEstimate.estimate_number.required" class="text-danger mt-1"> {{ $tc('estimates.errors.required') }}  </span> | ||||
|               <span v-show="$v.estimateNumAttribute.$error && !$v.estimateNumAttribute.required" class="text-danger mt-1"> {{ $tc('estimates.errors.required') }}  </span> | ||||
|               <span v-show="!$v.estimateNumAttribute.numeric" class="text-danger mt-1"> {{ $tc('validation.numbers_only') }}  </span> | ||||
|             </div> | ||||
|             <div class="col collapse-input"> | ||||
|               <label>{{ $t('estimates.ref_number') }}</label> | ||||
| @ -320,7 +321,7 @@ import { validationMixin } from 'vuelidate' | ||||
| import Guid from 'guid' | ||||
| import TaxStub from '../../stub/tax' | ||||
| import Tax from './EstimateTax' | ||||
| const { required, between, maxLength } = require('vuelidate/lib/validators') | ||||
| const { required, between, maxLength, numeric } = require('vuelidate/lib/validators') | ||||
|  | ||||
| export default { | ||||
|   components: { | ||||
| @ -361,7 +362,9 @@ export default { | ||||
|       discountPerItem: null, | ||||
|       initLoading: false, | ||||
|       isLoading: false, | ||||
|       maxDiscount: 0 | ||||
|       maxDiscount: 0, | ||||
|       estimatePrefix: null, | ||||
|       estimateNumAttribute: null | ||||
|     } | ||||
|   }, | ||||
|   validations () { | ||||
| @ -373,9 +376,6 @@ export default { | ||||
|         expiry_date: { | ||||
|           required | ||||
|         }, | ||||
|         estimate_number: { | ||||
|           required | ||||
|         }, | ||||
|         discount_val: { | ||||
|           between: between(0, this.subtotal) | ||||
|         }, | ||||
| @ -388,6 +388,10 @@ export default { | ||||
|       }, | ||||
|       selectedCustomer: { | ||||
|         required | ||||
|       }, | ||||
|       estimateNumAttribute: { | ||||
|         required, | ||||
|         numeric | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
| @ -559,6 +563,8 @@ export default { | ||||
|           this.taxPerItem = response.data.tax_per_item | ||||
|           this.selectedCurrency = this.defaultCurrency | ||||
|           this.estimateTemplates = response.data.estimateTemplates | ||||
|           this.estimatePrefix = response.data.estimate_prefix | ||||
|           this.estimateNumAttribute = response.data.nextEstimateNumber | ||||
|         } | ||||
|         this.initLoading = false | ||||
|         return | ||||
| @ -574,8 +580,9 @@ export default { | ||||
|         let today = new Date() | ||||
|         this.newEstimate.estimate_date = moment(today).toString() | ||||
|         this.newEstimate.expiry_date = moment(today).add(7, 'days').toString() | ||||
|         this.newEstimate.estimate_number = response.data.nextEstimateNumber | ||||
|         this.itemList = response.data.items | ||||
|         this.estimatePrefix = response.data.estimate_prefix | ||||
|         this.estimateNumAttribute = response.data.nextEstimateNumberAttribute | ||||
|       } | ||||
|       this.initLoading = false | ||||
|     }, | ||||
| @ -604,6 +611,7 @@ export default { | ||||
|       } | ||||
|  | ||||
|       this.isLoading = true | ||||
|       this.newEstimate.estimate_number = this.estimatePrefix + '-' + this.estimateNumAttribute | ||||
|  | ||||
|       let data = { | ||||
|         ...this.newEstimate, | ||||
| @ -637,7 +645,11 @@ export default { | ||||
|         this.isLoading = false | ||||
|       }).catch((err) => { | ||||
|         this.isLoading = false | ||||
|         console.log(err) | ||||
|         if (err.response.data.errors.estimate_number) { | ||||
|           window.toastr['error'](err.response.data.errors.estimate_number) | ||||
|           return true | ||||
|         } | ||||
|         window.toastr['error'](err.response.data.message) | ||||
|       }) | ||||
|     }, | ||||
|     submitUpdate (data) { | ||||
| @ -650,7 +662,11 @@ export default { | ||||
|         this.isLoading = false | ||||
|       }).catch((err) => { | ||||
|         this.isLoading = false | ||||
|         console.log(err) | ||||
|         if (err.response.data.errors.estimate_number) { | ||||
|           window.toastr['error'](err.response.data.errors.estimate_number) | ||||
|           return true | ||||
|         } | ||||
|         window.toastr['error'](err.response.data.message) | ||||
|       }) | ||||
|     }, | ||||
|     checkItemsData (index, isValid) { | ||||
|  | ||||
| @ -151,6 +151,7 @@ export default { | ||||
|       id: null, | ||||
|       count: null, | ||||
|       estimates: [], | ||||
|       estimate: null, | ||||
|       currency: null, | ||||
|       searchData: { | ||||
|         orderBy: null, | ||||
| @ -165,10 +166,6 @@ export default { | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     estimate () { | ||||
|       return this.$store.getters['estimate/getEstimate'](this.$route.params.id) | ||||
|     }, | ||||
|  | ||||
|     getOrderBy () { | ||||
|       if (this.searchData.orderBy === 'asc' || this.searchData.orderBy == null) { | ||||
|         return true | ||||
| @ -180,8 +177,14 @@ export default { | ||||
|       return `/estimates/pdf/${this.estimate.unique_hash}` | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     $route (to, from) { | ||||
|       this.loadEstimate() | ||||
|     } | ||||
|   }, | ||||
|   created () { | ||||
|     this.loadEstimates() | ||||
|     this.loadEstimate() | ||||
|     this.onSearched = _.debounce(this.onSearched, 500) | ||||
|   }, | ||||
|   methods: { | ||||
| @ -192,7 +195,8 @@ export default { | ||||
|       'markAsSent', | ||||
|       'sendEmail', | ||||
|       'deleteEstimate', | ||||
|       'selectEstimate' | ||||
|       'selectEstimate', | ||||
|       'fetchViewEstimate' | ||||
|     ]), | ||||
|     async loadEstimates () { | ||||
|       let response = await this.fetchEstimates() | ||||
| @ -200,6 +204,13 @@ export default { | ||||
|         this.estimates = response.data.estimates.data | ||||
|       } | ||||
|     }, | ||||
|     async loadEstimate () { | ||||
|       let response = await this.fetchViewEstimate(this.$route.params.id) | ||||
|  | ||||
|       if (response.data) { | ||||
|         this.estimate = response.data.estimate | ||||
|       } | ||||
|     }, | ||||
|     async onSearched () { | ||||
|       let data = '' | ||||
|       if (this.searchData.searchText !== '' && this.searchData.searchText !== null && this.searchData.searchText !== undefined) { | ||||
|  | ||||
| @ -30,8 +30,8 @@ | ||||
|           <div | ||||
|             v-if="selectedCustomer" class="show-customer"> | ||||
|             <div class="row px-2 mt-1"> | ||||
|               <div class="col col-6"> | ||||
|                 <div v-if="selectedCustomer.billing_address" class="row address-menu"> | ||||
|               <div v-if="selectedCustomer.billing_address" class="col col-6"> | ||||
|                 <div class="row address-menu"> | ||||
|                   <label class="col-sm-4 px-2 title">{{ $t('general.bill_to') }}</label> | ||||
|                   <div class="col-sm p-0 px-2 content"> | ||||
|                     <label v-if="selectedCustomer.billing_address.name"> | ||||
| @ -55,8 +55,8 @@ | ||||
|                   </div> | ||||
|                 </div> | ||||
|               </div> | ||||
|               <div class="col col-6"> | ||||
|                 <div v-if="selectedCustomer.shipping_address" class="row address-menu"> | ||||
|               <div v-if="selectedCustomer.shipping_address" class="col col-6"> | ||||
|                 <div class="row address-menu"> | ||||
|                   <label class="col-sm-4 px-2 title">{{ $t('general.ship_to') }}</label> | ||||
|                   <div class="col-sm p-0 px-2 content"> | ||||
|                     <label v-if="selectedCustomer.shipping_address.name"> | ||||
| @ -127,14 +127,15 @@ | ||||
|           <div class="row mt-4"> | ||||
|             <div class="col collapse-input"> | ||||
|               <label>{{ $t('invoices.invoice_number') }}<span class="text-danger"> * </span></label> | ||||
|               <base-input | ||||
|                 :invalid="$v.newInvoice.invoice_number.$error" | ||||
|                 :read-only="true" | ||||
|                 v-model="newInvoice.invoice_number" | ||||
|               <base-prefix-input | ||||
|                 v-model="invoiceNumAttribute" | ||||
|                 :invalid="$v.invoiceNumAttribute.$error" | ||||
|                 :prefix="invoicePrefix" | ||||
|                 icon="hashtag" | ||||
|                 @input="$v.newInvoice.invoice_number.$touch()" | ||||
|                 @input="$v.invoiceNumAttribute.$touch()" | ||||
|               /> | ||||
|               <span v-show="$v.newInvoice.invoice_number.$error && !$v.newInvoice.invoice_number.required" class="text-danger mt-1"> {{ $tc('validation.required') }}  </span> | ||||
|               <span v-show="$v.invoiceNumAttribute.$error && !$v.invoiceNumAttribute.required" class="text-danger mt-1"> {{ $tc('validation.required') }}  </span> | ||||
|               <span v-show="!$v.invoiceNumAttribute.numeric" class="text-danger mt-1"> {{ $tc('validation.numbers_only') }}  </span> | ||||
|             </div> | ||||
|             <div class="col collapse-input"> | ||||
|               <label>{{ $t('invoices.ref_number') }}</label> | ||||
| @ -320,7 +321,7 @@ import { validationMixin } from 'vuelidate' | ||||
| import Guid from 'guid' | ||||
| import TaxStub from '../../stub/tax' | ||||
| import Tax from './InvoiceTax' | ||||
| const { required, between, maxLength } = require('vuelidate/lib/validators') | ||||
| const { required, between, maxLength, numeric } = require('vuelidate/lib/validators') | ||||
|  | ||||
| export default { | ||||
|   components: { | ||||
| @ -361,7 +362,9 @@ export default { | ||||
|       discountPerItem: null, | ||||
|       initLoading: false, | ||||
|       isLoading: false, | ||||
|       maxDiscount: 0 | ||||
|       maxDiscount: 0, | ||||
|       invoicePrefix: null, | ||||
|       invoiceNumAttribute: null | ||||
|     } | ||||
|   }, | ||||
|   validations () { | ||||
| @ -373,9 +376,6 @@ export default { | ||||
|         due_date: { | ||||
|           required | ||||
|         }, | ||||
|         invoice_number: { | ||||
|           required | ||||
|         }, | ||||
|         discount_val: { | ||||
|           between: between(0, this.subtotal) | ||||
|         }, | ||||
| @ -388,6 +388,10 @@ export default { | ||||
|       }, | ||||
|       selectedCustomer: { | ||||
|         required | ||||
|       }, | ||||
|       invoiceNumAttribute: { | ||||
|         required, | ||||
|         numeric | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
| @ -559,6 +563,8 @@ export default { | ||||
|           this.taxPerItem = response.data.tax_per_item | ||||
|           this.selectedCurrency = this.defaultCurrency | ||||
|           this.invoiceTemplates = response.data.invoiceTemplates | ||||
|           this.invoicePrefix = response.data.invoice_prefix | ||||
|           this.invoiceNumAttribute = response.data.nextInvoiceNumber | ||||
|         } | ||||
|         this.initLoading = false | ||||
|         return | ||||
| @ -574,8 +580,9 @@ export default { | ||||
|         let today = new Date() | ||||
|         this.newInvoice.invoice_date = moment(today).toString() | ||||
|         this.newInvoice.due_date = moment(today).add(7, 'days').toString() | ||||
|         this.newInvoice.invoice_number = response.data.nextInvoiceNumber | ||||
|         this.itemList = response.data.items | ||||
|         this.invoicePrefix = response.data.invoice_prefix | ||||
|         this.invoiceNumAttribute = response.data.nextInvoiceNumberAttribute | ||||
|       } | ||||
|       this.initLoading = false | ||||
|     }, | ||||
| @ -604,6 +611,7 @@ export default { | ||||
|       } | ||||
|  | ||||
|       this.isLoading = true | ||||
|       this.newInvoice.invoice_number = this.invoicePrefix + '-' + this.invoiceNumAttribute | ||||
|  | ||||
|       let data = { | ||||
|         ...this.newInvoice, | ||||
| @ -637,6 +645,10 @@ export default { | ||||
|         this.isLoading = false | ||||
|       }).catch((err) => { | ||||
|         this.isLoading = false | ||||
|         if (err.response.data.errors.invoice_number) { | ||||
|           window.toastr['error'](err.response.data.errors.invoice_number) | ||||
|           return true | ||||
|         } | ||||
|         console.log(err) | ||||
|       }) | ||||
|     }, | ||||
| @ -653,6 +665,10 @@ export default { | ||||
|         } | ||||
|       }).catch((err) => { | ||||
|         this.isLoading = false | ||||
|         if (err.response.data.errors.invoice_number) { | ||||
|           window.toastr['error'](err.response.data.errors.invoice_number) | ||||
|           return true | ||||
|         } | ||||
|         console.log(err) | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
| @ -154,6 +154,7 @@ export default { | ||||
|       id: null, | ||||
|       count: null, | ||||
|       invoices: [], | ||||
|       invoice: null, | ||||
|       currency: null, | ||||
|       searchData: { | ||||
|         orderBy: null, | ||||
| @ -167,9 +168,7 @@ export default { | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     invoice () { | ||||
|       return this.$store.getters['invoice/getInvoice'](this.$route.params.id) | ||||
|     }, | ||||
|  | ||||
|     getOrderBy () { | ||||
|       if (this.searchData.orderBy === 'asc' || this.searchData.orderBy == null) { | ||||
|         return true | ||||
| @ -180,8 +179,14 @@ export default { | ||||
|       return `/invoices/pdf/${this.invoice.unique_hash}` | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     $route (to, from) { | ||||
|       this.loadInvoice() | ||||
|     } | ||||
|   }, | ||||
|   created () { | ||||
|     this.loadInvoices() | ||||
|     this.loadInvoice() | ||||
|     this.onSearch = _.debounce(this.onSearch, 500) | ||||
|   }, | ||||
|   methods: { | ||||
| @ -192,7 +197,8 @@ export default { | ||||
|       'markAsSent', | ||||
|       'sendEmail', | ||||
|       'deleteInvoice', | ||||
|       'selectInvoice' | ||||
|       'selectInvoice', | ||||
|       'fetchViewInvoice' | ||||
|     ]), | ||||
|     async loadInvoices () { | ||||
|       let response = await this.fetchInvoices() | ||||
| @ -200,6 +206,13 @@ export default { | ||||
|         this.invoices = response.data.invoices.data | ||||
|       } | ||||
|     }, | ||||
|     async loadInvoice () { | ||||
|       let response = await this.fetchViewInvoice(this.$route.params.id) | ||||
|  | ||||
|       if (response.data) { | ||||
|         this.invoice = response.data.invoice | ||||
|       } | ||||
|     }, | ||||
|     async onSearch () { | ||||
|       let data = '' | ||||
|       if (this.searchData.searchText !== '' && this.searchData.searchText !== null && this.searchData.searchText !== undefined) { | ||||
|  | ||||
| @ -50,7 +50,7 @@ export default { | ||||
|     Layout.set('layout-default') | ||||
|   }, | ||||
|  | ||||
|   created() { | ||||
|   created () { | ||||
|     this.bootstrap().then((res) => { | ||||
|       this.setInitialCompany() | ||||
|     }) | ||||
| @ -59,7 +59,7 @@ export default { | ||||
|   methods: { | ||||
|     ...mapActions(['bootstrap']), | ||||
|     ...mapActions('company', ['setSelectedCompany']), | ||||
|     setInitialCompany() { | ||||
|     setInitialCompany () { | ||||
|       let selectedCompany = Ls.get('selectedCompany') !== null | ||||
|  | ||||
|       if (selectedCompany) { | ||||
|  | ||||
| @ -40,16 +40,15 @@ | ||||
|             <div class="col-sm-6"> | ||||
|               <div class="form-group"> | ||||
|                 <label class="form-label">{{ $t('payments.payment_number') }}</label><span class="text-danger"> *</span> | ||||
|                 <base-input | ||||
|                   :invalid="$v.formData.payment_number.$error" | ||||
|                   v-model.trim="formData.payment_number" | ||||
|                   read-only | ||||
|                   type="text" | ||||
|                   name="email" | ||||
|                   @input="$v.formData.payment_number.$touch()" | ||||
|                 <base-prefix-input | ||||
|                   :invalid="$v.paymentNumAttribute.$error" | ||||
|                   v-model.trim="paymentNumAttribute" | ||||
|                   :prefix="paymentPrefix" | ||||
|                   @input="$v.paymentNumAttribute.$touch()" | ||||
|                 /> | ||||
|                 <div v-if="$v.formData.payment_number.$error"> | ||||
|                   <span v-if="!$v.formData.payment_number.required" class="text-danger">{{ $tc('validation.required') }}</span> | ||||
|                 <div v-if="$v.paymentNumAttribute.$error"> | ||||
|                   <span v-if="!$v.paymentNumAttribute.required" class="text-danger">{{ $tc('validation.required') }}</span> | ||||
|                   <span v-if="!$v.paymentNumAttribute.numeric" class="text-danger">{{ $tc('validation.numbers_only') }}</span> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
| @ -155,7 +154,7 @@ import { mapActions, mapGetters } from 'vuex' | ||||
| import MultiSelect from 'vue-multiselect' | ||||
| import { validationMixin } from 'vuelidate' | ||||
| import moment from 'moment' | ||||
| const { required, between, maxLength } = require('vuelidate/lib/validators') | ||||
| const { required, between, maxLength, numeric } = require('vuelidate/lib/validators') | ||||
|  | ||||
| export default { | ||||
|   components: { MultiSelect }, | ||||
| @ -184,7 +183,9 @@ export default { | ||||
|       invoiceList: [], | ||||
|       isLoading: false, | ||||
|       maxPayableAmount: Number.MAX_SAFE_INTEGER, | ||||
|       isSettingInitialData: true | ||||
|       isSettingInitialData: true, | ||||
|       paymentNumAttribute: null, | ||||
|       paymentPrefix: '' | ||||
|     } | ||||
|   }, | ||||
|   validations () { | ||||
| @ -193,9 +194,6 @@ export default { | ||||
|         required | ||||
|       }, | ||||
|       formData: { | ||||
|         payment_number: { | ||||
|           required | ||||
|         }, | ||||
|         payment_date: { | ||||
|           required | ||||
|         }, | ||||
| @ -206,6 +204,10 @@ export default { | ||||
|         notes: { | ||||
|           maxLength: maxLength(255) | ||||
|         } | ||||
|       }, | ||||
|       paymentNumAttribute: { | ||||
|         required, | ||||
|         numeric | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
| @ -297,6 +299,8 @@ export default { | ||||
|         this.customer = response.data.payment.user | ||||
|         this.formData.payment_date = moment(response.data.payment.payment_date, 'YYYY-MM-DD').toString() | ||||
|         this.formData.amount = parseFloat(response.data.payment.amount) | ||||
|         this.paymentPrefix = response.data.payment_prefix | ||||
|         this.paymentNumAttribute = response.data.nextPaymentNumber | ||||
|         if (response.data.payment.invoice !== null) { | ||||
|           this.maxPayableAmount = parseInt(response.data.payment.amount) + parseInt(response.data.payment.invoice.due_amount) | ||||
|           this.invoice = response.data.payment.invoice | ||||
| @ -305,7 +309,8 @@ export default { | ||||
|       } else { | ||||
|         let response = await this.fetchCreatePayment() | ||||
|         this.customerList = response.data.customers | ||||
|         this.formData.payment_number = response.data.nextPaymentNumber | ||||
|         this.paymentNumAttribute = response.data.nextPaymentNumberAttribute | ||||
|         this.paymentPrefix = response.data.payment_prefix | ||||
|         this.formData.payment_date = moment(new Date()).toString() | ||||
|       } | ||||
|       return true | ||||
| @ -332,6 +337,9 @@ export default { | ||||
|       if (this.$v.$invalid) { | ||||
|         return true | ||||
|       } | ||||
|  | ||||
|       this.formData.payment_number = this.paymentPrefix + '-' + this.paymentNumAttribute | ||||
|  | ||||
|       if (this.isEdit) { | ||||
|         let data = { | ||||
|           editData: { | ||||
| @ -340,6 +348,7 @@ export default { | ||||
|           }, | ||||
|           id: this.$route.params.id | ||||
|         } | ||||
|         try { | ||||
|           let response = await this.updatePayment(data) | ||||
|           if (response.data.success) { | ||||
|             window.toastr['success'](this.$t('payments.updated_message')) | ||||
| @ -351,12 +360,21 @@ export default { | ||||
|             return false | ||||
|           } | ||||
|           window.toastr['error'](response.data.error) | ||||
|         } catch (err) { | ||||
|           this.isLoading = false | ||||
|           if (err.response.data.errors.payment_number) { | ||||
|             window.toastr['error'](err.response.data.errors.payment_number) | ||||
|             return true | ||||
|           } | ||||
|           window.toastr['error'](err.response.data.message) | ||||
|         } | ||||
|       } else { | ||||
|         let data = { | ||||
|           ...this.formData, | ||||
|           payment_date: moment(this.formData.payment_date).format('DD/MM/YYYY') | ||||
|         } | ||||
|         this.isLoading = true | ||||
|         try { | ||||
|           let response = await this.addPayment(data) | ||||
|           if (response.data.success) { | ||||
|             window.toastr['success'](this.$t('payments.created_message')) | ||||
| @ -369,6 +387,14 @@ export default { | ||||
|             return false | ||||
|           } | ||||
|           window.toastr['error'](response.data.error) | ||||
|         } catch (err) { | ||||
|           this.isLoading = false | ||||
|           if (err.response.data.errors.payment_number) { | ||||
|             window.toastr['error'](err.response.data.errors.payment_number) | ||||
|             return true | ||||
|           } | ||||
|           window.toastr['error'](err.response.data.message) | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
							
								
								
									
										385
									
								
								resources/assets/js/views/settings/Customization.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										385
									
								
								resources/assets/js/views/settings/Customization.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,385 @@ | ||||
| <template> | ||||
|   <div class="setting-main-container customization"> | ||||
|     <div class="card setting-card"> | ||||
|       <ul class="tabs"> | ||||
|         <li class="tab" @click="setActiveTab('INVOICES')"> | ||||
|           <a :class="['tab-link', {'a-active': activeTab === 'INVOICES'}]" href="#">{{ $t('settings.customization.invoices.title') }}</a> | ||||
|         </li> | ||||
|         <li class="tab" @click="setActiveTab('ESTIMATES')"> | ||||
|           <a :class="['tab-link', {'a-active': activeTab === 'ESTIMATES'}]" href="#">{{ $t('settings.customization.estimates.title') }}</a> | ||||
|         </li> | ||||
|         <li class="tab" @click="setActiveTab('PAYMENTS')"> | ||||
|           <a :class="['tab-link', {'a-active': activeTab === 'PAYMENTS'}]" href="#">{{ $t('settings.customization.payments.title') }}</a> | ||||
|         </li> | ||||
|       </ul> | ||||
|  | ||||
|       <!-- Invoices Tab --> | ||||
|       <transition name="fade-customize"> | ||||
|         <div v-if="activeTab === 'INVOICES'" class="invoice-tab"> | ||||
|           <form action="" class="form-section" @submit.prevent="updateInvoiceSetting"> | ||||
|             <div class="row"> | ||||
|               <div class="col-md-12 mb-4"> | ||||
|                 <label class="input-label">{{ $t('settings.customization.invoices.invoice_prefix') }}</label> | ||||
|                 <base-input | ||||
|                   v-model="invoices.invoice_prefix" | ||||
|                   :invalid="$v.invoices.invoice_prefix.$error" | ||||
|                   class="prefix-input" | ||||
|                   @input="$v.invoices.invoice_prefix.$touch()" | ||||
|                   @keyup="changeToUppercase('INVOICES')" | ||||
|                 /> | ||||
|                 <span v-show="!$v.invoices.invoice_prefix.required" class="text-danger mt-1">{{ $t('validation.required') }}</span> | ||||
|                 <span v-if="!$v.invoices.invoice_prefix.maxLength" class="text-danger">{{ $t('validation.prefix_maxlength') }}</span> | ||||
|                 <span v-if="!$v.invoices.invoice_prefix.alpha" class="text-danger">{{ $t('validation.characters_only') }}</span> | ||||
|               </div> | ||||
|             </div> | ||||
|             <div class="row mb-3"> | ||||
|               <div class="col-md-12"> | ||||
|                 <base-button | ||||
|                   icon="save" | ||||
|                   color="theme" | ||||
|                   type="submit" | ||||
|                 > | ||||
|                   {{ $t('settings.customization.save') }} | ||||
|                 </base-button> | ||||
|               </div> | ||||
|             </div> | ||||
|             <hr> | ||||
|           </form> | ||||
|           <div class="col-md-12 mt-3"> | ||||
|             <div class="page-header"> | ||||
|               <h3 class="page-title"> | ||||
|                 {{ $t('settings.customization.invoices.invoice_settings') }} | ||||
|               </h3> | ||||
|               <div class="flex-box"> | ||||
|                 <div class="left"> | ||||
|                   <base-switch | ||||
|                     v-model="invoiceAutogenerate" | ||||
|                     class="btn-switch" | ||||
|                     @change="setInvoiceSetting" | ||||
|                   /> | ||||
|                 </div> | ||||
|                 <div class="right ml-15"> | ||||
|                   <p class="box-title">  {{ $t('settings.customization.invoices.autogenerate_invoice_number') }} </p> | ||||
|                   <p class="box-desc">  {{ $t('settings.customization.invoices.invoice_setting_description') }} </p> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </transition> | ||||
|  | ||||
|       <!-- Estimates Tab --> | ||||
|       <transition name="fade-customize"> | ||||
|         <div v-if="activeTab === 'ESTIMATES'" class="estimate-tab"> | ||||
|           <form action="" class="form-section" @submit.prevent="updateEstimateSetting"> | ||||
|             <div class="row"> | ||||
|               <div class="col-md-12 mb-4"> | ||||
|                 <label class="input-label">{{ $t('settings.customization.estimates.estimate_prefix') }}</label> | ||||
|                 <base-input | ||||
|                   v-model="estimates.estimate_prefix" | ||||
|                   :invalid="$v.estimates.estimate_prefix.$error" | ||||
|                   class="prefix-input" | ||||
|                   @input="$v.estimates.estimate_prefix.$touch()" | ||||
|                   @keyup="changeToUppercase('ESTIMATES')" | ||||
|                 /> | ||||
|                 <span v-show="!$v.estimates.estimate_prefix.required" class="text-danger mt-1">{{ $t('validation.required') }}</span> | ||||
|                 <span v-if="!$v.estimates.estimate_prefix.maxLength" class="text-danger">{{ $t('validation.prefix_maxlength') }}</span> | ||||
|                 <span v-if="!$v.estimates.estimate_prefix.alpha" class="text-danger">{{ $t('validation.characters_only') }}</span> | ||||
|               </div> | ||||
|             </div> | ||||
|             <div class="row mb-3"> | ||||
|               <div class="col-md-12"> | ||||
|                 <base-button | ||||
|                   icon="save" | ||||
|                   color="theme" | ||||
|                   type="submit" | ||||
|                 > | ||||
|                   {{ $t('settings.customization.save') }} | ||||
|                 </base-button> | ||||
|               </div> | ||||
|             </div> | ||||
|             <hr> | ||||
|           </form> | ||||
|           <div class="col-md-12 mt-3"> | ||||
|             <div class="page-header"> | ||||
|               <h3 class="page-title"> | ||||
|                 {{ $t('settings.customization.estimates.estimate_settings') }} | ||||
|               </h3> | ||||
|               <div class="flex-box"> | ||||
|                 <div class="left"> | ||||
|                   <base-switch | ||||
|                     v-model="estimateAutogenerate" | ||||
|                     class="btn-switch" | ||||
|                     @change="setEstimateSetting" | ||||
|                   /> | ||||
|                 </div> | ||||
|                 <div class="right ml-15"> | ||||
|                   <p class="box-title">  {{ $t('settings.customization.estimates.autogenerate_estimate_number') }} </p> | ||||
|                   <p class="box-desc">  {{ $t('settings.customization.estimates.estimate_setting_description') }} </p> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </transition> | ||||
|  | ||||
|       <!-- Payments Tab --> | ||||
|       <transition name="fade-customize"> | ||||
|         <div v-if="activeTab === 'PAYMENTS'" class="payment-tab"> | ||||
|           <form action="" class="form-section" @submit.prevent="updatePaymentSetting"> | ||||
|             <div class="row"> | ||||
|               <div class="col-md-12 mb-4"> | ||||
|                 <label class="input-label">{{ $t('settings.customization.payments.payment_prefix') }}</label> | ||||
|                 <base-input | ||||
|                   v-model="payments.payment_prefix" | ||||
|                   :invalid="$v.payments.payment_prefix.$error" | ||||
|                   class="prefix-input" | ||||
|                   @input="$v.payments.payment_prefix.$touch()" | ||||
|                   @keyup="changeToUppercase('PAYMENTS')" | ||||
|                 /> | ||||
|                 <span v-show="!$v.payments.payment_prefix.required" class="text-danger mt-1">{{ $t('validation.required') }}</span> | ||||
|                 <span v-if="!$v.payments.payment_prefix.maxLength" class="text-danger">{{ $t('validation.prefix_maxlength') }}</span> | ||||
|                 <span v-if="!$v.payments.payment_prefix.alpha" class="text-danger">{{ $t('validation.characters_only') }}</span> | ||||
|               </div> | ||||
|             </div> | ||||
|             <div class="row mb-3"> | ||||
|               <div class="col-md-12"> | ||||
|                 <base-button | ||||
|                   icon="save" | ||||
|                   color="theme" | ||||
|                   type="submit" | ||||
|                 > | ||||
|                   {{ $t('settings.customization.save') }} | ||||
|                 </base-button> | ||||
|               </div> | ||||
|             </div> | ||||
|           </form> | ||||
|           <hr> | ||||
|           <div class="col-md-12 mt-4"> | ||||
|             <div class="page-header"> | ||||
|               <h3 class="page-title"> | ||||
|                 {{ $t('settings.customization.payments.payment_settings') }} | ||||
|               </h3> | ||||
|               <div class="flex-box"> | ||||
|                 <div class="left"> | ||||
|                   <base-switch | ||||
|                     v-model="paymentAutogenerate" | ||||
|                     class="btn-switch" | ||||
|                     @change="setPaymentSetting" | ||||
|                   /> | ||||
|                 </div> | ||||
|                 <div class="right ml-15"> | ||||
|                   <p class="box-title">  {{ $t('settings.customization.payments.autogenerate_payment_number') }} </p> | ||||
|                   <p class="box-desc">  {{ $t('settings.customization.payments.payment_setting_description') }} </p> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </transition> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| <script> | ||||
| import { validationMixin } from 'vuelidate' | ||||
| const { required, maxLength, alpha } = require('vuelidate/lib/validators') | ||||
| export default { | ||||
|   mixins: [validationMixin], | ||||
|   data () { | ||||
|     return { | ||||
|       activeTab: 'INVOICES', | ||||
|       invoiceAutogenerate: false, | ||||
|       estimateAutogenerate: false, | ||||
|       paymentAutogenerate: false, | ||||
|       invoices: { | ||||
|         invoice_prefix: null, | ||||
|         invoice_notes: null, | ||||
|         invoice_terms_and_conditions: null | ||||
|       }, | ||||
|       estimates: { | ||||
|         estimate_prefix: null, | ||||
|         estimate_notes: null, | ||||
|         estimate_terms_and_conditions: null | ||||
|       }, | ||||
|       payments: { | ||||
|         payment_prefix: null | ||||
|       }, | ||||
|       currentData: null | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     activeTab () { | ||||
|       this.loadData() | ||||
|     } | ||||
|   }, | ||||
|   validations: { | ||||
|     invoices: { | ||||
|       invoice_prefix: { | ||||
|         required, | ||||
|         maxLength: maxLength(5), | ||||
|         alpha | ||||
|       } | ||||
|     }, | ||||
|     estimates: { | ||||
|       estimate_prefix: { | ||||
|         required, | ||||
|         maxLength: maxLength(5), | ||||
|         alpha | ||||
|       } | ||||
|     }, | ||||
|     payments: { | ||||
|       payment_prefix: { | ||||
|         required, | ||||
|         maxLength: maxLength(5), | ||||
|         alpha | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   created () { | ||||
|     this.loadData() | ||||
|   }, | ||||
|   methods: { | ||||
|     async setInvoiceSetting () { | ||||
|       let data = { | ||||
|         key: 'invoice_auto_generate', | ||||
|         value: this.invoiceAutogenerate ? 'YES' : 'NO' | ||||
|       } | ||||
|       let response = await window.axios.put('/api/settings/update-setting', data) | ||||
|       if (response.data) { | ||||
|         window.toastr['success'](this.$t('general.setting_updated')) | ||||
|       } | ||||
|     }, | ||||
|     async setEstimateSetting () { | ||||
|       let data = { | ||||
|         key: 'estimate_auto_generate', | ||||
|         value: this.estimateAutogenerate ? 'YES' : 'NO' | ||||
|       } | ||||
|       let response = await window.axios.put('/api/settings/update-setting', data) | ||||
|       if (response.data) { | ||||
|         window.toastr['success'](this.$t('general.setting_updated')) | ||||
|       } | ||||
|     }, | ||||
|     changeToUppercase (currentTab) { | ||||
|       if (currentTab === 'INVOICES') { | ||||
|         this.invoices.invoice_prefix = this.invoices.invoice_prefix.toUpperCase() | ||||
|         return true | ||||
|       } | ||||
|  | ||||
|       if (currentTab === 'ESTIMATES') { | ||||
|         this.estimates.estimate_prefix = this.estimates.estimate_prefix.toUpperCase() | ||||
|         return true | ||||
|       } | ||||
|  | ||||
|       if (currentTab === 'PAYMENTS') { | ||||
|         this.payments.payment_prefix = this.payments.payment_prefix.toUpperCase() | ||||
|         return true | ||||
|       } | ||||
|     }, | ||||
|     async setPaymentSetting () { | ||||
|       let data = { | ||||
|         key: 'payment_auto_generate', | ||||
|         value: this.paymentAutogenerate ? 'YES' : 'NO' | ||||
|       } | ||||
|       let response = await window.axios.put('/api/settings/update-setting', data) | ||||
|       if (response.data) { | ||||
|         window.toastr['success'](this.$t('general.setting_updated')) | ||||
|       } | ||||
|     }, | ||||
|     async loadData () { | ||||
|       let res = await window.axios.get('/api/settings/get-customize-setting') | ||||
|  | ||||
|       if (res.data) { | ||||
|         this.invoices.invoice_prefix = res.data.invoice_prefix | ||||
|         this.invoices.invoice_notes = res.data.invoice_notes | ||||
|         this.invoices.invoice_terms_and_conditions = res.data.invoice_terms_and_conditions | ||||
|         this.estimates.estimate_prefix = res.data.estimate_prefix | ||||
|         this.estimates.estimate_notes = res.data.estimate_notes | ||||
|         this.estimates.estimate_terms_and_conditions = res.data.estimate_terms_and_conditions | ||||
|         this.payments.payment_prefix = res.data.payment_prefix | ||||
|  | ||||
|         if (res.data.invoice_auto_generate === 'YES') { | ||||
|           this.invoiceAutogenerate = true | ||||
|         } else { | ||||
|           this.invoiceAutogenerate = false | ||||
|         } | ||||
|  | ||||
|         if (res.data.estimate_auto_generate === 'YES') { | ||||
|           this.estimateAutogenerate = true | ||||
|         } else { | ||||
|           this.estimateAutogenerate = false | ||||
|         } | ||||
|  | ||||
|         if (res.data.payment_auto_generate === 'YES') { | ||||
|           this.paymentAutogenerate = true | ||||
|         } else { | ||||
|           this.paymentAutogenerate = false | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     async updateInvoiceSetting () { | ||||
|       this.$v.invoices.$touch() | ||||
|  | ||||
|       if (this.$v.invoices.$invalid) { | ||||
|         return false | ||||
|       } | ||||
|  | ||||
|       let data = {type: 'INVOICES', ...this.invoices} | ||||
|  | ||||
|       if (this.updateSetting(data)) { | ||||
|         window.toastr['success'](this.$t('settings.customization.invoices.invoice_setting_updated')) | ||||
|       } | ||||
|     }, | ||||
|     async updateEstimateSetting () { | ||||
|       this.$v.estimates.$touch() | ||||
|  | ||||
|       if (this.$v.estimates.$invalid) { | ||||
|         return false | ||||
|       } | ||||
|  | ||||
|       let data = {type: 'ESTIMATES', ...this.estimates} | ||||
|  | ||||
|       if (this.updateSetting(data)) { | ||||
|         window.toastr['success'](this.$t('settings.customization.estimates.estimate_setting_updated')) | ||||
|       } | ||||
|     }, | ||||
|     async updatePaymentSetting () { | ||||
|       this.$v.payments.$touch() | ||||
|  | ||||
|       if (this.$v.payments.$invalid) { | ||||
|         return false | ||||
|       } | ||||
|  | ||||
|       let data = {type: 'PAYMENTS', ...this.payments} | ||||
|  | ||||
|       if (this.updateSetting(data)) { | ||||
|         window.toastr['success'](this.$t('settings.customization.payments.payment_setting_updated')) | ||||
|       } | ||||
|     }, | ||||
|     async updateSetting (data) { | ||||
|       let res = await window.axios.put('/api/settings/update-customize-setting', data) | ||||
|  | ||||
|       if (res.data.success) { | ||||
|         return true | ||||
|       } | ||||
|  | ||||
|       return false | ||||
|     }, | ||||
|     setActiveTab (val) { | ||||
|       this.activeTab = val | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| <style> | ||||
|   .fade-customize-enter-active { | ||||
|     transition: opacity 0.9s; | ||||
|   } | ||||
|  | ||||
|   .fade-customize-leave-active  { | ||||
|     transition: opacity 0s; | ||||
|   } | ||||
|  | ||||
|   .fade-customize-enter, .fade-customize-leave-to /* .fade-leave-active below version 2.1.8 */ { | ||||
|     opacity: 0; | ||||
|   } | ||||
| </style> | ||||
| @ -14,6 +14,7 @@ | ||||
|             <base-select | ||||
|               v-model="formData.currency" | ||||
|               :options="currencies" | ||||
|               :custom-label="currencyNameWithCode" | ||||
|               :class="{'error': $v.formData.currency.$error }" | ||||
|               :searchable="true" | ||||
|               :show-labels="false" | ||||
| @ -179,6 +180,9 @@ export default { | ||||
|     this.getDiscountSettings() | ||||
|   }, | ||||
|   methods: { | ||||
|     currencyNameWithCode ({name, code}) { | ||||
|       return `${code} - ${name}` | ||||
|     }, | ||||
|     ...mapActions('currency', [ | ||||
|       'setDefaultCurrency' | ||||
|     ]), | ||||
|  | ||||
| @ -45,6 +45,12 @@ export default { | ||||
|           icon: 'building', | ||||
|           iconType: 'far' | ||||
|         }, | ||||
|         { | ||||
|           link: '/admin/settings/customization', | ||||
|           title: 'settings.menu_title.customization', | ||||
|           icon: 'edit', | ||||
|           iconType: 'fa' | ||||
|         }, | ||||
|         { | ||||
|           link: '/admin/settings/preferences', | ||||
|           title: 'settings.menu_title.preferences', | ||||
|  | ||||
| @ -203,8 +203,10 @@ export default { | ||||
|           this.$emit('next') | ||||
|           window.toastr['success'](this.$t('wizard.success.' + response.data.success)) | ||||
|           return true | ||||
|         } else { | ||||
|         } else if (response.data.error) { | ||||
|           window.toastr['error'](this.$t('wizard.errors.' + response.data.error)) | ||||
|         } else if (response.data.error_message) { | ||||
|           window.toastr['error'](response.data.error_message) | ||||
|         } | ||||
|       } catch (e) { | ||||
|         window.toastr['error'](e.response.data.message) | ||||
|  | ||||
| @ -11,6 +11,7 @@ | ||||
|             v-model="settingData.currency" | ||||
|             :class="{'error': $v.settingData.currency.$error }" | ||||
|             :options="currencies" | ||||
|             :custom-label="currencyNameWithCode" | ||||
|             :searchable="true" | ||||
|             :show-labels="false" | ||||
|             :placeholder="$t('settings.currencies.select_currency')" | ||||
| @ -150,6 +151,9 @@ export default { | ||||
|     this.getOnboardingData() | ||||
|   }, | ||||
|   methods: { | ||||
|     currencyNameWithCode ({name, code}) { | ||||
|       return `${code} - ${name}` | ||||
|     }, | ||||
|     ...mapActions('auth', [ | ||||
|       'loginOnBoardingUser' | ||||
|     ]), | ||||
|  | ||||
| @ -18,7 +18,7 @@ | ||||
|           </div> | ||||
|         </div> | ||||
|         <avatar-cropper | ||||
|           :labels="{ submit: 'submit', cancel: 'Cancle'}" | ||||
|           :labels="{ submit: 'submit', cancel: 'Cancel'}" | ||||
|           :cropper-options="cropperOptions" | ||||
|           :output-options="cropperOutputOptions" | ||||
|           :output-quality="0.8" | ||||
| @ -54,7 +54,7 @@ | ||||
|           /> | ||||
|           <div v-if="$v.profileData.email.$error"> | ||||
|             <span v-if="!$v.profileData.email.required" class="text-danger">{{ $tc('validation.required') }}</span> | ||||
|             <span v-if="!$v.profileData.email.email" class="text-danger">{{ $tc('validation.required') }}</span> | ||||
|             <span v-if="!$v.profileData.email.email" class="text-danger">{{ $tc('validation.email_incorrect') }}</span> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
| @ -145,7 +145,7 @@ export default { | ||||
|       }, | ||||
|       password: { | ||||
|         required, | ||||
|         minLength: minLength(5) | ||||
|         minLength: minLength(8) | ||||
|       }, | ||||
|       confirm_password: { | ||||
|         required: requiredIf('isRequired'), | ||||
|  | ||||
							
								
								
									
										54
									
								
								resources/assets/sass/components/base/base-prefix-input.scss
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								resources/assets/sass/components/base/base-prefix-input.scss
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| .base-prefix-input { | ||||
|     display: flex; | ||||
|     position: relative; | ||||
|     width: 100%; | ||||
|     height: 40px; | ||||
|     padding: 2px 2px; | ||||
|     flex-direction: row; | ||||
|     background: #FFFFFF; | ||||
|     border: 1px solid $ls-color-gray--light; | ||||
|     border-radius: 5px; | ||||
|  | ||||
|     .icon { | ||||
|         width: 13px; | ||||
|         height: 18px; | ||||
|         color: $ls-color-gray; | ||||
|         font-style: normal; | ||||
|         font-weight: 900; | ||||
|         font-size: 14px; | ||||
|         line-height: 16px; | ||||
|         margin-top: 17px; | ||||
|         margin-left: 20px; | ||||
|         z-index: 1; | ||||
|         transform: translate(-50%,-50%); | ||||
|     } | ||||
|  | ||||
|     p { | ||||
|         padding: 0 0 0 0; | ||||
|         margin: 0 0 0 0; | ||||
|     } | ||||
|  | ||||
|     .prefix-label { | ||||
|         display: flex; | ||||
|         height: 18px; | ||||
|         color: #55547A; | ||||
|         font-weight: 500; | ||||
|         font-size: 14px; | ||||
|         line-height: 16px; | ||||
|         padding: 9px 2px 9px 10px; | ||||
|     } | ||||
|  | ||||
|     .prefix-input-field { | ||||
|         width: 100%; | ||||
|         padding: 8px 13px; | ||||
|         padding-left: 1px; | ||||
|         text-align: left; | ||||
|         background: #FFFFFF; | ||||
|         border: none; | ||||
|         font-style: normal; | ||||
|         font-weight: 400; | ||||
|         font-size: 14px; | ||||
|         line-height: 21px; | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										2
									
								
								resources/assets/sass/crater.scss
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								resources/assets/sass/crater.scss
									
									
									
									
										vendored
									
									
								
							| @ -48,6 +48,7 @@ | ||||
| @import 'components/base/base-text-area'; | ||||
| @import "components/base/base-switch"; | ||||
| @import 'components/base/base-loader/index'; | ||||
| @import 'components/base/base-prefix-input'; | ||||
|  | ||||
|  | ||||
| // Components | ||||
| @ -91,6 +92,7 @@ | ||||
| @import 'pages/login'; | ||||
| @import 'pages/login-3'; | ||||
| @import 'pages/404'; | ||||
| @import 'pages/customization'; | ||||
| @import 'pages/settings'; | ||||
| @import 'pages/invoices/create'; | ||||
| @import 'pages/invoices/view'; | ||||
|  | ||||
							
								
								
									
										39
									
								
								resources/assets/sass/pages/customization.scss
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								resources/assets/sass/pages/customization.scss
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| .customization { | ||||
|  | ||||
|     .prefix-input { | ||||
|         max-width: 30%; | ||||
|     } | ||||
|  | ||||
|     .form-section { | ||||
|         padding: 8px 15px; | ||||
|     } | ||||
|  | ||||
|     .invoice-customization-card { | ||||
|         border: 1px solid #EBF1FA;border-radius: 5px; | ||||
|     } | ||||
|  | ||||
|     @media (max-width: $x-small-breakpoint) { | ||||
|         .address-customization-card { | ||||
|  | ||||
|             .address-fields-container { | ||||
|                 display: flex; | ||||
|                 flex-wrap: wrap; | ||||
|  | ||||
|                 .fields-list { | ||||
|                     border-right: 0px; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         .tabs { | ||||
|  | ||||
|             .tab { | ||||
|                 padding: 10px 10px; | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -3,9 +3,11 @@ | ||||
| <head> | ||||
|     <title>Estimate</title> | ||||
|     {{-- <link href="https://fonts.googleapis.com/css?family=Poppins&display=swap" rel="stylesheet"> --}} | ||||
|     <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | ||||
|  | ||||
|     <style type="text/css"> | ||||
|         body { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-family: "DejaVu Sans"; | ||||
|         } | ||||
|  | ||||
|         html { | ||||
| @ -58,13 +60,11 @@ | ||||
|             margin-left:160px; | ||||
|         } | ||||
|         .header { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-size: 20px; | ||||
|             color: rgba(0, 0, 0, 0.7); | ||||
|         } | ||||
|  | ||||
|         .TextColor1 { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-size: 16px; | ||||
|             color: rgba(0, 0, 0, 0.5); | ||||
|         } | ||||
| @ -339,7 +339,6 @@ | ||||
|         } | ||||
|  | ||||
|         .notes { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-style: normal; | ||||
|             font-weight: 300; | ||||
|             font-size: 12px; | ||||
| @ -352,7 +351,6 @@ | ||||
|         } | ||||
|  | ||||
|         .notes-label { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-style: normal; | ||||
|             font-weight: normal; | ||||
|             font-size: 15px; | ||||
| @ -410,7 +408,7 @@ | ||||
|             <div class="bill-address-container"> | ||||
|                 @include('app.pdf.estimate.partials.billing-address') | ||||
|             </div> | ||||
|             @if($estimate->user->billingaddress->name || $estimate->user->billingaddress->address_street_1 || $estimate->user->billingaddress->address_street_2 || $estimate->user->billingaddress->country || $estimate->user->billingaddress->state || $estimate->user->billingaddress->city || $estimate->user->billingaddress->zip || $estimate->user->billingaddress->phone) | ||||
|             @if($estimate->user->billingaddress) | ||||
|                 <div class="ship-address-container"> | ||||
|             @else | ||||
|                 <div class="ship-address-container " style="float:left;padding-left:0px;"> | ||||
|  | ||||
| @ -3,9 +3,11 @@ | ||||
| <head> | ||||
|     <title>Estimate</title> | ||||
|     {{-- <link href="https://fonts.googleapis.com/css?family=Poppins&display=swap" rel="stylesheet"> --}} | ||||
|     <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | ||||
|  | ||||
|     <style type="text/css"> | ||||
|         body { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-family: "DejaVu Sans"; | ||||
|         } | ||||
|  | ||||
|         html { | ||||
| @ -62,13 +64,11 @@ | ||||
|             margin-left:160px; | ||||
|         } | ||||
|         .header { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-size: 20px; | ||||
|             color: rgba(0, 0, 0, 0.7); | ||||
|         } | ||||
|  | ||||
|         .TextColor1 { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-size: 16px; | ||||
|             color: rgba(0, 0, 0, 0.5); | ||||
|         } | ||||
| @ -364,7 +364,6 @@ | ||||
|         } | ||||
|  | ||||
|         .notes { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-style: normal; | ||||
|             font-weight: 300; | ||||
|             font-size: 12px; | ||||
| @ -377,7 +376,6 @@ | ||||
|         } | ||||
|  | ||||
|         .notes-label { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-style: normal; | ||||
|             font-weight: normal; | ||||
|             font-size: 15px; | ||||
| @ -421,7 +419,7 @@ | ||||
|             <div class="ship-address-container"> | ||||
|                 @include('app.pdf.estimate.partials.shipping-address') | ||||
|             </div> | ||||
|             @if($estimate->user->shippingaddress->name || $estimate->user->shippingaddress->address_street_1 || $estimate->user->shippingaddress->address_street_2 || $estimate->user->shippingaddress->country || $estimate->user->shippingaddress->state || $estimate->user->shippingaddress->city || $estimate->user->shippingaddress->zip || $estimate->user->phone) | ||||
|             @if($estimate->user->shippingaddress) | ||||
|                 <div class="bill-address-container"> | ||||
|             @else | ||||
|                 <div class="bill-address-container" style="float:right;padding-right:0px;"> | ||||
|  | ||||
| @ -3,9 +3,11 @@ | ||||
| <head> | ||||
|     <title>Estimate</title> | ||||
|     {{-- <link href="https://fonts.googleapis.com/css?family=Poppins&display=swap" rel="stylesheet"> --}} | ||||
|     <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | ||||
|  | ||||
|     <style type="text/css"> | ||||
|         body { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-family: "DejaVu Sans"; | ||||
|         } | ||||
|  | ||||
|         html { | ||||
| @ -64,13 +66,11 @@ | ||||
|             margin-left:160px; | ||||
|         } | ||||
|         .header { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-size: 20px; | ||||
|             color: rgba(0, 0, 0, 0.7); | ||||
|         } | ||||
|  | ||||
|         .TextColor1 { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-size: 16px; | ||||
|             color: rgba(0, 0, 0, 0.5); | ||||
|         } | ||||
| @ -372,7 +372,6 @@ | ||||
|         } | ||||
|  | ||||
|         .notes { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-style: normal; | ||||
|             font-weight: 300; | ||||
|             font-size: 12px; | ||||
| @ -385,7 +384,6 @@ | ||||
|         } | ||||
|  | ||||
|         .notes-label { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-style: normal; | ||||
|             font-weight: normal; | ||||
|             font-size: 15px; | ||||
| @ -426,7 +424,7 @@ | ||||
|                 <div style="float:left;"> | ||||
|                     @include('app.pdf.estimate.partials.billing-address') | ||||
|                 </div> | ||||
|                 @if($estimate->user->billingaddress->name || $estimate->user->billingaddress->address_street_1 || $estimate->user->billingaddress->address_street_2 || $estimate->user->billingaddress->country || $estimate->user->billingaddress->state || $estimate->user->billingaddress->city || $estimate->user->billingaddress->zip || $estimate->user->billingaddress->phone) | ||||
|                @if($estimate->user->billingaddress) | ||||
|                     <div style="float:right;"> | ||||
|                 @else | ||||
|                     <div style="float:left;"> | ||||
|  | ||||
| @ -1,7 +1,5 @@ | ||||
| @if($estimate->user->billingaddress) | ||||
|     @if($estimate->user->billingaddress->name || $estimate->user->billingaddress->address_street_1 || $estimate->user->billingaddress->address_street_2 || $estimate->user->billingaddress->country || $estimate->user->billingaddress->state || $estimate->user->billingaddress->city || $estimate->user->billingaddress->zip || $estimate->user->billingaddress->phone) | ||||
|     <p class="bill-to">Bill To,</p> | ||||
|     @endif | ||||
|     @if($estimate->user->billingaddress->name) | ||||
|         <p class="bill-user-name"> | ||||
|             {{$estimate->user->billingaddress->name}} | ||||
| @ -16,11 +14,11 @@ | ||||
|             {{$estimate->user->billingaddress->address_street_2}}<br> | ||||
|         @endif | ||||
|  | ||||
|         @if($estimate->user->billingaddress->city && $estimate->user->billingaddress->city) | ||||
|         @if($estimate->user->billingaddress->city) | ||||
|             {{$estimate->user->billingaddress->city}}, | ||||
|         @endif | ||||
|  | ||||
|         @if($estimate->user->billingaddress->state && $estimate->user->billingaddress->state) | ||||
|         @if($estimate->user->billingaddress->state) | ||||
|             {{$estimate->user->billingaddress->state}}. | ||||
|         @endif | ||||
|  | ||||
|  | ||||
| @ -1,7 +1,5 @@ | ||||
| @if($estimate->user->shippingaddress) | ||||
|     @if($estimate->user->shippingaddress->name || $estimate->user->shippingaddress->address_street_1 || $estimate->user->shippingaddress->address_street_2 || $estimate->user->shippingaddress->country || $estimate->user->shippingaddress->state || $estimate->user->shippingaddress->city || $estimate->user->shippingaddress->zip || $estimate->user->phone) | ||||
|     <p class="ship-to">Ship To,</p> | ||||
|     @endif | ||||
|     @if($estimate->user->shippingaddress->name) | ||||
|         <p class="ship-user-name"> | ||||
|             {{$estimate->user->shippingaddress->name}} | ||||
|  | ||||
| @ -18,24 +18,48 @@ | ||||
|     @endphp | ||||
|     @foreach ($estimate->items as $item) | ||||
|         <tr class="item-details"> | ||||
|             <td class="inv-item items" style="text-align: right; color: #040405; padding-right: 20px; vertical-align: top;">{{$index}}</td> | ||||
|             <td class="inv-item items" style="text-align: left; color: #040405;padding-left: 0px"> | ||||
|                 <span>{{ $item->name }}</span><br> | ||||
|                 <span style="text-align: left; color: #595959; font-size: 9px; font-weight:300; line-height: 12px;">{{ $item->description }}</span> | ||||
|             <td | ||||
|                 class="inv-item items" | ||||
|                 style="text-align: right; color: #040405; padding-right: 20px; vertical-align: top;" | ||||
|             > | ||||
|                 {{$index}} | ||||
|             </td> | ||||
|             <td | ||||
|                 class="inv-item items" | ||||
|                 style="text-align: left; color: #040405;padding-left: 0px" | ||||
|             > | ||||
|                 <span>{{ $item->name }}</span><br> | ||||
|                 <span | ||||
|                     style="text-align: left; color: #595959; font-size: 9px; font-weight:300; line-height: 12px;" | ||||
|                 > | ||||
|                     {{ $item->description }} | ||||
|                 </span> | ||||
|             </td> | ||||
|             <td | ||||
|                 class="inv-item items" | ||||
|                 style="text-align: right; color: #040405; padding-right: 20px" | ||||
|             > | ||||
|                 {{$item->quantity}} | ||||
|             </td> | ||||
|             <td | ||||
|                 class="inv-item items" | ||||
|                 style="text-align: right; color: #040405; padding-right: 40px" | ||||
|             > | ||||
|                 {!! format_money_pdf($item->price, $estimate->user->currency) !!} | ||||
|             </td> | ||||
|             <td class="inv-item items" style="text-align: right; color: #040405; padding-right: 20px">{{$item->quantity}}</td> | ||||
|             <td class="inv-item items" style="text-align: right; color: #040405; padding-right: 40px">{{$item->price/100}}</td> | ||||
|             @if($estimate->discount_per_item === 'YES') | ||||
|                 <td class="inv-item items" style="text-align: right; color: #040405; padding-left: 10px"> | ||||
|                     @if($item->discount_type === 'fixed') | ||||
|                         {{$item->discount_val/100}} | ||||
|                         {!! format_money_pdf($item->discount_val, $estimate->user->currency) !!} | ||||
|                     @endif | ||||
|                     @if($item->discount_type === 'percentage') | ||||
|                         {{$item->discount}}% | ||||
|                     @endif | ||||
|                 </td> | ||||
|             @endif | ||||
|             <td class="inv-item items" style="text-align: right; color: #040405;">{{$item->total/100}}</td> | ||||
|             <td class="inv-item items" style="text-align: right; color: #040405;"> | ||||
|                 {!! format_money_pdf($item->total, $estimate->user->currency) !!} | ||||
|             </td> | ||||
|         </tr> | ||||
|         @php | ||||
|             $index += 1 | ||||
| @ -47,7 +71,7 @@ | ||||
|     <tr> | ||||
|         <td class="no-borde" style="color: #55547A; padding-left:10px;  font-size:12px;">Subtotal</td> | ||||
|         <td class="no-border items" | ||||
|             style="padding-right:10px; text-align: right;  font-size:12px; color: #040405; font-weight: 500;">{!! format_money_pdf($estimate->sub_total) !!}</td> | ||||
|             style="padding-right:10px; text-align: right;  font-size:12px; color: #040405; font-weight: 500;">{!! format_money_pdf($estimate->sub_total, $estimate->user->currency) !!}</td> | ||||
|     </tr> | ||||
|  | ||||
|     @if ($estimate->tax_per_item === 'YES') | ||||
| @ -57,7 +81,7 @@ | ||||
|                     {{$labels[$i]}} | ||||
|                 </td> | ||||
|                 <td class="no-border items padd2" style="padding-right:10px; font-weight: 500; text-align: right; font-size:12px;  color: #040405"> | ||||
|                     {!! format_money_pdf($taxes[$i]) !!} | ||||
|                     {!! format_money_pdf($taxes[$i], $estimate->user->currency) !!} | ||||
|                 </td> | ||||
|             </tr> | ||||
|         @endfor | ||||
| @ -68,7 +92,7 @@ | ||||
|                     {{$tax->name.' ('.$tax->percent.'%)'}} | ||||
|                 </td> | ||||
|                 <td class="no-border items padd2" style="padding-right:10px; font-weight: 500; text-align: right; font-size:12px;  color: #040405"> | ||||
|                     {!! format_money_pdf($tax->amount) !!} | ||||
|                     {!! format_money_pdf($tax->amount, $estimate->user->currency) !!} | ||||
|                 </td> | ||||
|             </tr> | ||||
|         @endforeach | ||||
| @ -77,14 +101,19 @@ | ||||
|     @if ($estimate->discount_per_item === 'NO') | ||||
|         <tr> | ||||
|             <td class="no-border" style="padding-left:10px; text-align:left; font-size:12px; color: #55547A;"> | ||||
|                 @if($estimate->discount_type === 'fixed') | ||||
|                     Discount | ||||
|                 @endif | ||||
|                 @if($estimate->discount_type === 'percentage') | ||||
|                     Discount ({{$estimate->discount}}%) | ||||
|                 @endif | ||||
|             </td> | ||||
|             <td class="no-border items padd2" style="padding-right:10px; font-weight: 500; text-align: right; font-size:12px;  color: #040405"> | ||||
|                 @if($estimate->discount_type === 'fixed') | ||||
|                     {!! format_money_pdf($estimate->discount_val) !!} | ||||
|                     {!! format_money_pdf($estimate->discount_val, $estimate->user->currency) !!} | ||||
|                 @endif | ||||
|                 @if($estimate->discount_type === 'percentage') | ||||
|                     {!! format_money_pdf($estimate->discount_val) !!} | ||||
|                     {!! format_money_pdf($estimate->discount_val, $estimate->user->currency) !!} | ||||
|                 @endif | ||||
|             </td> | ||||
|         </tr> | ||||
| @ -103,7 +132,7 @@ | ||||
|             class="no-border total-border-right items padd8" | ||||
|             style="padding-right:10px; font-weight: 500; text-align: right; font-size:12px;  padding-top:20px; color: #5851DB" | ||||
|         > | ||||
|             {!! format_money_pdf($estimate->total)!!} | ||||
|             {!! format_money_pdf($estimate->total, $estimate->user->currency)!!} | ||||
|         </td> | ||||
|     </tr> | ||||
| </table> | ||||
|  | ||||
| @ -3,9 +3,11 @@ | ||||
| <head> | ||||
|     <title>Invoice</title> | ||||
|     {{-- <link href="https://fonts.googleapis.com/css?family=Poppins&display=swap" rel="stylesheet"> --}} | ||||
|     <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | ||||
|  | ||||
|     <style type="text/css"> | ||||
|         body { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-family: "DejaVu Sans"; | ||||
|         } | ||||
|  | ||||
|         html { | ||||
| @ -59,13 +61,11 @@ | ||||
|             margin-left:160px; | ||||
|         } | ||||
|         .header { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-size: 20px; | ||||
|             color: rgba(0, 0, 0, 0.7); | ||||
|         } | ||||
|  | ||||
|         .TextColor1 { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-size: 16px; | ||||
|             color: rgba(0, 0, 0, 0.5); | ||||
|         } | ||||
| @ -346,7 +346,6 @@ | ||||
|         } | ||||
|  | ||||
|         .notes { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-style: normal; | ||||
|             font-weight: 300; | ||||
|             font-size: 12px; | ||||
| @ -359,7 +358,6 @@ | ||||
|         } | ||||
|  | ||||
|         .notes-label { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-style: normal; | ||||
|             font-weight: normal; | ||||
|             font-size: 15px; | ||||
| @ -416,9 +414,8 @@ | ||||
|         <div class="bill-add"> | ||||
|             <div class="bill-address-container"> | ||||
|                     @include('app.pdf.invoice.partials.billing-address') | ||||
|  | ||||
|             </div> | ||||
|             @if($invoice->user->billingaddress->name || $invoice->user->billingaddress->address_street_1 || $invoice->user->billingaddress->address_street_2 || $invoice->user->billingaddress->country || $invoice->user->billingaddress->state || $invoice->user->billingaddress->city || $invoice->user->billingaddress->zip || $invoice->user->billingaddress->phone) | ||||
|             @if($invoice->user->billingaddress) | ||||
|                 <div class="ship-address-container"> | ||||
|             @else | ||||
|                 <div class="ship-address-container " style="float:left;padding-left:0px;"> | ||||
|  | ||||
| @ -3,9 +3,10 @@ | ||||
| <head> | ||||
|     <title>Invoice</title> | ||||
|     {{-- <link href="https://fonts.googleapis.com/css?family=Poppins&display=swap" rel="stylesheet"> --}} | ||||
|     <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | ||||
|     <style type="text/css"> | ||||
|         body { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-family: "DejaVu Sans"; | ||||
|         } | ||||
|  | ||||
|         html { | ||||
| @ -61,13 +62,11 @@ | ||||
|             margin-left:160px; | ||||
|         } | ||||
|         .header { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-size: 20px; | ||||
|             color: rgba(0, 0, 0, 0.7); | ||||
|         } | ||||
|  | ||||
|         .TextColor1 { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-size: 16px; | ||||
|             color: rgba(0, 0, 0, 0.5); | ||||
|         } | ||||
| @ -373,7 +372,6 @@ | ||||
|         } | ||||
|  | ||||
|         .notes { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-style: normal; | ||||
|             font-weight: 300; | ||||
|             font-size: 12px; | ||||
| @ -386,7 +384,6 @@ | ||||
|         } | ||||
|  | ||||
|         .notes-label { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-style: normal; | ||||
|             font-weight: normal; | ||||
|             font-size: 15px; | ||||
| @ -431,7 +428,7 @@ | ||||
|             <div class="ship-address-container"> | ||||
|                 @include('app.pdf.invoice.partials.shipping-address') | ||||
|             </div> | ||||
|             @if($invoice->user->shippingaddress->name || $invoice->user->shippingaddress->address_street_1 || $invoice->user->shippingaddress->address_street_2 || $invoice->user->shippingaddress->country || $invoice->user->shippingaddress->state || $invoice->user->shippingaddress->city || $invoice->user->shippingaddress->zip || $invoice->user->phone) | ||||
|             @if($invoice->user->shippingaddress) | ||||
|                 <div class="bill-address-container"> | ||||
|             @else | ||||
|                 <div class="bill-address-container" style="float:right;padding-right:0px;"> | ||||
|  | ||||
| @ -3,9 +3,11 @@ | ||||
| <head> | ||||
|     <title>Invoice</title> | ||||
|     {{-- <link href="https://fonts.googleapis.com/css?family=Poppins&display=swap" rel="stylesheet"> --}} | ||||
|     <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | ||||
|  | ||||
|     <style type="text/css"> | ||||
|         body { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-family: "DejaVu Sans"; | ||||
|         } | ||||
|  | ||||
|         html { | ||||
| @ -64,13 +66,11 @@ | ||||
|             margin-left:160px; | ||||
|         } | ||||
|         .header { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-size: 20px; | ||||
|             color: rgba(0, 0, 0, 0.7); | ||||
|         } | ||||
|  | ||||
|         .TextColor1 { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-size: 16px; | ||||
|             color: rgba(0, 0, 0, 0.5); | ||||
|         } | ||||
| @ -382,7 +382,6 @@ | ||||
|         } | ||||
|  | ||||
|         .notes { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-style: normal; | ||||
|             font-weight: 300; | ||||
|             font-size: 12px; | ||||
| @ -395,7 +394,6 @@ | ||||
|         } | ||||
|  | ||||
|         .notes-label { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-style: normal; | ||||
|             font-weight: normal; | ||||
|             font-size: 15px; | ||||
| @ -436,7 +434,7 @@ | ||||
|                 <div style="float:left;"> | ||||
|                     @include('app.pdf.invoice.partials.billing-address') | ||||
|                 </div> | ||||
|                 @if($invoice->user->billingaddress->name || $invoice->user->billingaddress->address_street_1 || $invoice->user->billingaddress->address_street_2 || $invoice->user->billingaddress->country || $invoice->user->billingaddress->state || $invoice->user->billingaddress->city || $invoice->user->billingaddress->zip || $invoice->user->billingaddress->phone) | ||||
|                 @if($invoice->user->billingaddress) | ||||
|                     <div style="float:right;"> | ||||
|                 @else | ||||
|                     <div style="float:left;"> | ||||
|  | ||||
| @ -1,7 +1,5 @@ | ||||
| @if($invoice->user->billingaddress) | ||||
|     @if($invoice->user->billingaddress->name || $invoice->user->billingaddress->address_street_1 || $invoice->user->billingaddress->address_street_2 || $invoice->user->billingaddress->country || $invoice->user->billingaddress->state || $invoice->user->billingaddress->city || $invoice->user->billingaddress->zip || $invoice->user->billingaddress->phone) | ||||
|     <p class="bill-to">Bill To,</p> | ||||
|     @endif | ||||
|     @if($invoice->user->billingaddress->name) | ||||
|         <p class="bill-user-name"> | ||||
|             {{$invoice->user->billingaddress->name}} | ||||
|  | ||||
| @ -3,6 +3,6 @@ | ||||
|         <div class="notes-label"> | ||||
|             Notes | ||||
|         </div> | ||||
|         {{$invoice->notes}} | ||||
|         {!! $invoice->notes !!} | ||||
|     </div> | ||||
| @endif | ||||
|  | ||||
| @ -1,7 +1,5 @@ | ||||
| @if($invoice->user->shippingaddress) | ||||
|     @if($invoice->user->shippingaddress->name || $invoice->user->shippingaddress->address_street_1 || $invoice->user->shippingaddress->address_street_2 || $invoice->user->shippingaddress->country || $invoice->user->shippingaddress->state || $invoice->user->shippingaddress->city || $invoice->user->shippingaddress->zip || $invoice->user->phone) | ||||
|     <p class="ship-to">Ship To,</p> | ||||
|     @endif | ||||
|     @if($invoice->user->shippingaddress->name) | ||||
|         <p class="ship-user-name"> | ||||
|             {{$invoice->user->shippingaddress->name}} | ||||
|  | ||||
| @ -18,24 +18,47 @@ | ||||
|     @endphp | ||||
|     @foreach ($invoice->items as $item) | ||||
|         <tr class="item-details"> | ||||
|             <td class="inv-item items" style="text-align: right; color: #040405; padding-right: 20px; vertical-align: top;">{{$index}}</td> | ||||
|             <td class="inv-item items" style="text-align: left; color: #040405;padding-left: 0px"> | ||||
|             <td | ||||
|                 class="inv-item items" | ||||
|                 style="text-align: right; color: #040405; padding-right: 20px; vertical-align: top;" | ||||
|             > | ||||
|                 {{$index}} | ||||
|             </td> | ||||
|             <td | ||||
|                 class="inv-item items" | ||||
|                 style="text-align: left; color: #040405;padding-left: 0px" | ||||
|             > | ||||
|                 <span>{{ $item->name }}</span><br> | ||||
|                 <span style="text-align: left; color: #595959; font-size: 9px; font-weight:300; line-height: 12px;">{{ $item->description }}</span> | ||||
|             </td> | ||||
|             <td class="inv-item items" style="text-align: right; color: #040405; padding-right: 20px">{{$item->quantity}}</td> | ||||
|             <td class="inv-item items" style="text-align: right; color: #040405; padding-right: 40px">{{$item->price/100}}</td> | ||||
|             <td | ||||
|                 class="inv-item items" | ||||
|                 style="text-align: right; color: #040405; padding-right: 20px" | ||||
|             > | ||||
|                 {{$item->quantity}} | ||||
|             </td> | ||||
|             <td | ||||
|                 class="inv-item items" | ||||
|                 style="text-align: right; color: #040405; padding-right: 40px" | ||||
|             > | ||||
|                 {!! format_money_pdf($item->price, $invoice->user->currency) !!} | ||||
|             </td> | ||||
|             @if($invoice->discount_per_item === 'YES') | ||||
|                 <td class="inv-item items" style="text-align: right; color: #040405; padding-left: 10px"> | ||||
|                     @if($item->discount_type === 'fixed') | ||||
|                         {{$item->discount_val/100}} | ||||
|                         {!! format_money_pdf($item->discount_val, $invoice->user->currency) !!} | ||||
|                     @endif | ||||
|                     @if($item->discount_type === 'percentage') | ||||
|                         {{$item->discount}}% | ||||
|                     @endif | ||||
|                 </td> | ||||
|             @endif | ||||
|             <td class="inv-item items" style="text-align: right; color: #040405;">{{$item->total/100}}</td> | ||||
|             <td | ||||
|                 class="inv-item items" | ||||
|                 style="text-align: right; color: #040405;" | ||||
|             > | ||||
|                 {!! format_money_pdf($item->total, $invoice->user->currency) !!} | ||||
|             </td> | ||||
|         </tr> | ||||
|         @php | ||||
|             $index += 1 | ||||
| @ -47,7 +70,7 @@ | ||||
|     <tr> | ||||
|         <td class="no-borde" style="color: #55547A; padding-left:10px;  font-size:12px;">Subtotal</td> | ||||
|         <td class="no-border items" | ||||
|             style="padding-right:10px; text-align: right;  font-size:12px; color: #040405; font-weight: 500;">{!! format_money_pdf($invoice->sub_total) !!}</td> | ||||
|             style="padding-right:10px; text-align: right;  font-size:12px; color: #040405; font-weight: 500;">{!! format_money_pdf($invoice->sub_total, $invoice->user->currency) !!}</td> | ||||
|     </tr> | ||||
|  | ||||
|     @if ($invoice->tax_per_item === 'YES') | ||||
| @ -57,7 +80,7 @@ | ||||
|                     {{$labels[$i]}} | ||||
|                 </td> | ||||
|                 <td class="no-border items padd2" style="padding-right:10px; font-weight: 500; text-align: right; font-size:12px;  color: #040405"> | ||||
|                     {!! format_money_pdf($taxes[$i]) !!} | ||||
|                     {!! format_money_pdf($taxes[$i], $invoice->user->currency) !!} | ||||
|                 </td> | ||||
|             </tr> | ||||
|         @endfor | ||||
| @ -68,7 +91,7 @@ | ||||
|                     {{$tax->name.' ('.$tax->percent.'%)'}} | ||||
|                 </td> | ||||
|                 <td class="no-border items padd2" style="padding-right:10px; font-weight: 500; text-align: right; font-size:12px;  color: #040405"> | ||||
|                     {!! format_money_pdf($tax->amount) !!} | ||||
|                     {!! format_money_pdf($tax->amount, $invoice->user->currency) !!} | ||||
|                 </td> | ||||
|             </tr> | ||||
|         @endforeach | ||||
| @ -77,14 +100,19 @@ | ||||
|     @if ($invoice->discount_per_item === 'NO') | ||||
|         <tr> | ||||
|             <td class="no-border" style="padding-left:10px; text-align:left; font-size:12px; color: #55547A;"> | ||||
|                 @if($invoice->discount_type === 'fixed') | ||||
|                     Discount | ||||
|                 @endif | ||||
|                 @if($invoice->discount_type === 'percentage') | ||||
|                     Discount ({{$invoice->discount}}%) | ||||
|                 @endif | ||||
|             </td> | ||||
|             <td class="no-border items padd2" style="padding-right:10px; font-weight: 500; text-align: right; font-size:12px;  color: #040405"> | ||||
|                 @if($invoice->discount_type === 'fixed') | ||||
|                     {!! format_money_pdf($invoice->discount_val) !!} | ||||
|                     {!! format_money_pdf($invoice->discount_val, $invoice->user->currency) !!} | ||||
|                 @endif | ||||
|                 @if($invoice->discount_type === 'percentage') | ||||
|                     {!! format_money_pdf($invoice->discount_val) !!} | ||||
|                     {!! format_money_pdf($invoice->discount_val, $invoice->user->currency) !!} | ||||
|                 @endif | ||||
|             </td> | ||||
|         </tr> | ||||
| @ -103,7 +131,7 @@ | ||||
|             class="no-border total-border-right items padd8" | ||||
|             style="padding-right:10px; font-weight: 500; text-align: right; font-size:12px;  padding-top:20px; color: #5851DB" | ||||
|         > | ||||
|             {!! format_money_pdf($invoice->total)!!} | ||||
|             {!! format_money_pdf($invoice->total, $invoice->user->currency)!!} | ||||
|         </td> | ||||
|     </tr> | ||||
| </table> | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|     {{-- <link href="https://fonts.googleapis.com/css?family=Poppins&display=swap" rel="stylesheet"> --}} | ||||
|     <style type="text/css"> | ||||
|         body { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-family: "DejaVu Sans"; | ||||
|         } | ||||
|  | ||||
|         /* html { | ||||
| @ -181,10 +181,14 @@ | ||||
|                     @foreach ($expenseCategories as $expenseCategory) | ||||
|                         <tr> | ||||
|                             <td> | ||||
|                                 <p class="expense-title">{{ $expenseCategory->category->name }}</p> | ||||
|                                 <p class="expense-title"> | ||||
|                                     {{ $expenseCategory->category->name }} | ||||
|                                 </p> | ||||
|                             </td> | ||||
|                             <td> | ||||
|                                 <p class="expense-money">{!! format_money_pdf($expenseCategory->total_amount) !!}</p> | ||||
|                                 <p class="expense-money"> | ||||
|                                     {!! format_money_pdf($expenseCategory->total_amount) !!} | ||||
|                                 </p> | ||||
|                             </td> | ||||
|                         </tr> | ||||
|                     @endforeach | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|     {{-- <link href="https://fonts.googleapis.com/css?family=Poppins&display=swap" rel="stylesheet"> --}} | ||||
|     <style type="text/css"> | ||||
|         body { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-family: "DejaVu Sans"; | ||||
|         } | ||||
|  | ||||
|         html { | ||||
| @ -220,10 +220,14 @@ | ||||
|                     @foreach ($expenseCategories as $expenseCategory) | ||||
|                         <tr> | ||||
|                             <td> | ||||
|                                 <p class="expense-title">{{ $expenseCategory->category->name }}</p> | ||||
|                                 <p class="expense-title"> | ||||
|                                     {{ $expenseCategory->category->name }} | ||||
|                                 </p> | ||||
|                             </td> | ||||
|                             <td> | ||||
|                                 <p class="expense-money">{!! format_money_pdf($expenseCategory->total_amount) !!}</p> | ||||
|                                 <p class="expense-money"> | ||||
|                                     {!! format_money_pdf($expenseCategory->total_amount) !!} | ||||
|                                 </p> | ||||
|                             </td> | ||||
|                         </tr> | ||||
|                     @endforeach | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|     {{-- <link href="https://fonts.googleapis.com/css?family=Poppins&display=swap" rel="stylesheet"> --}} | ||||
|     <style type="text/css"> | ||||
|         body { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-family: "DejaVu Sans"; | ||||
|         } | ||||
|  | ||||
|         /* html { | ||||
| @ -223,10 +223,14 @@ | ||||
|                         @foreach ($customer->invoices as $invoice) | ||||
|                             <tr> | ||||
|                                 <td> | ||||
|                                     <p class="expense-title">{{ $invoice->formattedInvoiceDate }} ({{ $invoice->invoice_number }})</p> | ||||
|                                     <p class="expense-title"> | ||||
|                                         {{ $invoice->formattedInvoiceDate }} ({{ $invoice->invoice_number }}) | ||||
|                                     </p> | ||||
|                                 </td> | ||||
|                                 <td> | ||||
|                                     <p class="expense-money">{!! format_money_pdf($invoice->total) !!}</p> | ||||
|                                     <p class="expense-money"> | ||||
|                                         {!! format_money_pdf($invoice->total) !!} | ||||
|                                     </p> | ||||
|                                 </td> | ||||
|                             </tr> | ||||
|                         @endforeach | ||||
| @ -235,7 +239,9 @@ | ||||
|                 <table class="expense-total-table"> | ||||
|                     <tr> | ||||
|                         <td class="expense-total-cell"> | ||||
|                             <p class="expense-total">{!! format_money_pdf($customer->totalAmount) !!}</p> | ||||
|                             <p class="expense-total"> | ||||
|                                 {!! format_money_pdf($customer->totalAmount) !!} | ||||
|                             </p> | ||||
|                         </td> | ||||
|                     </tr> | ||||
|                 </table> | ||||
| @ -249,7 +255,9 @@ | ||||
|                     <p class="profit-title">TOTAL SALES</p> | ||||
|                 </td> | ||||
|                 <td> | ||||
|                     <p class="profit-money">{!! format_money_pdf($totalAmount) !!}</p> | ||||
|                     <p class="profit-money"> | ||||
|                         {!! format_money_pdf($totalAmount) !!} | ||||
|                     </p> | ||||
|                 </td> | ||||
|             </tr> | ||||
|         </table> | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|     {{-- <link href="https://fonts.googleapis.com/css?family=Poppins&display=swap" rel="stylesheet"> --}} | ||||
|     <style type="text/css"> | ||||
|         body { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-family: "DejaVu Sans"; | ||||
|         } | ||||
|  | ||||
|         /* html { | ||||
| @ -222,10 +222,14 @@ | ||||
|                     <table class="expenses-table"> | ||||
|                         <tr> | ||||
|                             <td> | ||||
|                                 <p class="expense-title">{{ $item->name }}</p> | ||||
|                                 <p class="expense-title"> | ||||
|                                     {{ $item->name }} | ||||
|                                 </p> | ||||
|                             </td> | ||||
|                             <td> | ||||
|                                 <p class="expense-money">{!! format_money_pdf($item->total_amount) !!}</p> | ||||
|                                 <p class="expense-money"> | ||||
|                                     {!! format_money_pdf($item->total_amount) !!} | ||||
|                                 </p> | ||||
|                             </td> | ||||
|                         </tr> | ||||
|                     </table> | ||||
| @ -235,7 +239,9 @@ | ||||
|                 <table class="expense-total-table"> | ||||
|                     <tr> | ||||
|                         <td class="expense-total-cell"> | ||||
|                             <p class="expense-total">{!! format_money_pdf($totalAmount) !!}</p> | ||||
|                             <p class="expense-total"> | ||||
|                                 {!! format_money_pdf($totalAmount) !!} | ||||
|                             </p> | ||||
|                         </td> | ||||
|                     </tr> | ||||
|                 </table> | ||||
| @ -248,7 +254,9 @@ | ||||
|                     <p class="profit-title">TOTAL SALES</p> | ||||
|                 </td> | ||||
|                 <td> | ||||
|                     <p class="profit-money">{!! format_money_pdf($totalAmount) !!}</p> | ||||
|                     <p class="profit-money"> | ||||
|                         {!! format_money_pdf($totalAmount) !!} | ||||
|                     </p> | ||||
|                 </td> | ||||
|             </tr> | ||||
|         </table> | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|     {{-- <link href="https://fonts.googleapis.com/css?family=Poppins&display=swap" rel="stylesheet"> --}} | ||||
|     <style type="text/css"> | ||||
|         body { | ||||
|             font-family: 'Roboto', sans-serif; | ||||
|             font-family: "DejaVu Sans"; | ||||
|         } | ||||
|  | ||||
|         /* html { | ||||
| @ -174,10 +174,14 @@ | ||||
|             <table class="header"> | ||||
|                 <tr> | ||||
|                     <td> | ||||
|                         <p class="heading-text">{{ $company->name }}</p> | ||||
|                         <p class="heading-text"> | ||||
|                             {{ $company->name }} | ||||
|                         </p> | ||||
|                     </td> | ||||
|                     <td> | ||||
|                         <p class="heading-date-range">{{ $from_date }} - {{ $to_date }}</p> | ||||
|                         <p class="heading-date-range"> | ||||
|                             {{ $from_date }} - {{ $to_date }} | ||||
|                         </p> | ||||
|                     </td> | ||||
|                 </tr> | ||||
|                 <tr> | ||||
| @ -192,10 +196,14 @@ | ||||
|                     @foreach ($taxTypes as $tax) | ||||
|                         <tr> | ||||
|                             <td> | ||||
|                                 <p class="tax-title">{{ $tax->taxType->name }}</p> | ||||
|                                 <p class="tax-title"> | ||||
|                                     {{ $tax->taxType->name }} | ||||
|                                 </p> | ||||
|                             </td> | ||||
|                             <td> | ||||
|                                 <p class="tax-money">{!! format_money_pdf($tax->total_tax_amount) !!}</p> | ||||
|                                 <p class="tax-money"> | ||||
|                                     {!! format_money_pdf($tax->total_tax_amount) !!} | ||||
|                                 </p> | ||||
|                             </td> | ||||
|                         </tr> | ||||
|                     @endforeach | ||||
| @ -207,7 +215,9 @@ | ||||
|         <table class="tax-total-table"> | ||||
|             <tr> | ||||
|                 <td class="tax-total-cell"> | ||||
|                     <p class="tax-total">{!! format_money_pdf($totalTaxAmount) !!}</p> | ||||
|                     <p class="tax-total"> | ||||
|                         {!! format_money_pdf($totalTaxAmount) !!} | ||||
|                     </p> | ||||
|                 </td> | ||||
|             </tr> | ||||
|         </table> | ||||
| @ -217,7 +227,9 @@ | ||||
|                     <p class="total-tax-title">TOTAL TAX</p> | ||||
|                 </td> | ||||
|                 <td> | ||||
|                     <p class="total-tax-money">{!! format_money_pdf($totalTaxAmount) !!}</p> | ||||
|                     <p class="total-tax-money"> | ||||
|                         {!! format_money_pdf($totalTaxAmount) !!} | ||||
|                     </p> | ||||
|                 </td> | ||||
|             </tr> | ||||
|         </table> | ||||
|  | ||||
| @ -46,16 +46,6 @@ Route::get('/countries', [ | ||||
|     'uses' => 'LocationController@getCountries' | ||||
| ]); | ||||
|  | ||||
| Route::get('/states/{id}', [ | ||||
|     'as' => 'states', | ||||
|     'uses' => 'LocationController@getStates' | ||||
| ]); | ||||
|  | ||||
| Route::get('/cities/{id}', [ | ||||
|     'as' => 'cities', | ||||
|     'uses' => 'LocationController@getCities' | ||||
| ]); | ||||
|  | ||||
|  | ||||
| // Onboarding | ||||
| //---------------------------------- | ||||
| @ -358,6 +348,16 @@ Route::group(['middleware' => 'api'], function () { | ||||
|                 'uses' => 'CompanyController@updateSetting' | ||||
|             ]); | ||||
|  | ||||
|             Route::get('/get-customize-setting', [ | ||||
|                 'as' => 'admin.get.customize.setting', | ||||
|                 'uses' => 'CompanyController@getCustomizeSetting' | ||||
|             ]); | ||||
|  | ||||
|             Route::put('/update-customize-setting', [ | ||||
|                 'as' => 'admin.update.customize.setting', | ||||
|                 'uses' => 'CompanyController@updateCustomizeSetting' | ||||
|             ]); | ||||
|  | ||||
|             Route::get('/environment/mail', [ | ||||
|                 'as' => 'admin.environment.mail', | ||||
|                 'uses' => 'EnvironmentController@getMailDrivers' | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	