mirror of
				https://github.com/crater-invoice/crater.git
				synced 2025-10-27 19:51:09 -04:00 
			
		
		
		
	Merge branch 'fix-minor-issues' into 'master'
Fix minor issues See merge request mohit.panjvani/crater-web!1469
This commit is contained in:
		| @ -3,7 +3,6 @@ | ||||
| namespace Crater\Http\Requests; | ||||
|  | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Str; | ||||
| use Illuminate\Validation\Rule; | ||||
|  | ||||
| class CompaniesRequest extends FormRequest | ||||
| @ -34,6 +33,10 @@ class CompaniesRequest extends FormRequest | ||||
|             'currency' => [ | ||||
|                 'required' | ||||
|             ], | ||||
|             'slug' => [ | ||||
|                 'required', | ||||
|                 Rule::unique('companies') | ||||
|             ], | ||||
|             'address.name' => [ | ||||
|                 'nullable', | ||||
|             ], | ||||
| @ -68,11 +71,11 @@ class CompaniesRequest extends FormRequest | ||||
|     { | ||||
|         return collect($this->validated()) | ||||
|             ->only([ | ||||
|                 'name' | ||||
|                 'name', | ||||
|                 'slug' | ||||
|             ]) | ||||
|             ->merge([ | ||||
|                 'owner_id' => $this->user()->id, | ||||
|                 'slug' => Str::slug($this->name) | ||||
|                 'owner_id' => $this->user()->id | ||||
|             ]) | ||||
|             ->toArray(); | ||||
|     } | ||||
|  | ||||
| @ -30,7 +30,8 @@ class CompanyRequest extends FormRequest | ||||
|                 Rule::unique('companies')->ignore($this->header('company'), 'id'), | ||||
|             ], | ||||
|             'slug' => [ | ||||
|                 'nullable' | ||||
|                 'required', | ||||
|                 Rule::unique('companies')->ignore($this->header('company'), 'id'), | ||||
|             ], | ||||
|             'address.country_id' => [ | ||||
|                 'required', | ||||
|  | ||||
| @ -217,7 +217,7 @@ class Company extends Model implements HasMedia | ||||
|             'estimate_billing_address_format' => $billingAddressFormat, | ||||
|             'payment_company_address_format' => $companyAddressFormat, | ||||
|             'payment_from_customer_address_format' => $paymentFromCustomerAddress, | ||||
|             'currency' => request()->currency ?? 13, | ||||
|             'currency' => request()->currency ?? 1, | ||||
|             'time_zone' => 'Asia/Kolkata', | ||||
|             'language' => 'en', | ||||
|             'fiscal_year' => '1-12', | ||||
|  | ||||
| @ -48,6 +48,24 @@ | ||||
|             /> | ||||
|           </BaseInputGroup> | ||||
|  | ||||
|           <BaseInputGroup | ||||
|             :label="$tc('settings.company_info.company_slug')" | ||||
|             :help-text="$t('settings.company_info.company_slug_help_text')" | ||||
|             :error=" | ||||
|               v$.newCompanyForm.slug.$error && | ||||
|               v$.newCompanyForm.slug.$errors[0].$message | ||||
|             " | ||||
|             :content-loading="isFetchingInitialData" | ||||
|             required | ||||
|           > | ||||
|             <BaseInput | ||||
|               v-model="newCompanyForm.slug" | ||||
|               :invalid="v$.newCompanyForm.slug.$error" | ||||
|               :content-loading="isFetchingInitialData" | ||||
|               @input="v$.newCompanyForm.slug.$touch()" | ||||
|             /> | ||||
|           </BaseInputGroup> | ||||
|  | ||||
|           <BaseInputGroup | ||||
|             :content-loading="isFetchingInitialData" | ||||
|             :label="$tc('settings.company_info.country')" | ||||
| @ -130,7 +148,7 @@ | ||||
|  | ||||
| <script setup> | ||||
| import { useModalStore } from '@/scripts/stores/modal' | ||||
| import { computed, onMounted, ref, reactive } from 'vue' | ||||
| import { computed, onMounted, ref, reactive, watch } from 'vue' | ||||
| import { useI18n } from 'vue-i18n' | ||||
| import { required, minLength, helpers } from '@vuelidate/validators' | ||||
| import { useVuelidate } from '@vuelidate/core' | ||||
| @ -152,6 +170,7 @@ let companyLogoName = ref(null) | ||||
|  | ||||
| const newCompanyForm = reactive({ | ||||
|   name: null, | ||||
|   slug: null, | ||||
|   currency: '', | ||||
|   address: { | ||||
|     country_id: null, | ||||
| @ -162,6 +181,9 @@ const modalActive = computed(() => { | ||||
|   return modalStore.active && modalStore.componentName === 'CompanyModal' | ||||
| }) | ||||
|  | ||||
| const slugValidator = (value) => { | ||||
|   return value == slugify(value) | ||||
| } | ||||
| const rules = { | ||||
|   newCompanyForm: { | ||||
|     name: { | ||||
| @ -171,6 +193,17 @@ const rules = { | ||||
|         minLength(3) | ||||
|       ), | ||||
|     }, | ||||
|     slug: { | ||||
|       required: helpers.withMessage(t('validation.required'), required), | ||||
|       minLength: helpers.withMessage( | ||||
|         t('validation.name_min_length', { count: 3 }), | ||||
|         minLength(3) | ||||
|       ), | ||||
|       slugValidator: helpers.withMessage( | ||||
|         t('validation.invalid_slug'), | ||||
|         slugValidator | ||||
|       ), | ||||
|     }, | ||||
|     address: { | ||||
|       country_id: { | ||||
|         required: helpers.withMessage(t('validation.required'), required), | ||||
| @ -243,6 +276,7 @@ async function submitCompanyData() { | ||||
|  | ||||
| function resetNewCompanyForm() { | ||||
|   newCompanyForm.name = '' | ||||
|   newCompanyForm.slug = '' | ||||
|   newCompanyForm.currency = '' | ||||
|   newCompanyForm.address.country_id = '' | ||||
|  | ||||
| @ -257,4 +291,24 @@ function closeCompanyModal() { | ||||
|     v$.value.$reset() | ||||
|   }, 300) | ||||
| } | ||||
|  | ||||
| // watcher for if change company name then auto fill company slug value | ||||
| watch( | ||||
|   () => newCompanyForm.name, | ||||
|   (currentValue) => { | ||||
|     newCompanyForm.slug = slugify(currentValue) | ||||
|   } | ||||
| ) | ||||
|  | ||||
| function slugify(string) { | ||||
|   return string | ||||
|     .toString() | ||||
|     .trim() | ||||
|     .toLowerCase() | ||||
|     .replace(/\s+/g, '-') | ||||
|     .replace(/[^\w\-]+/g, '') | ||||
|     .replace(/\-\-+/g, '-') | ||||
|     .replace(/^-+/, '') | ||||
|     .replace(/-+$/, '') | ||||
| } | ||||
| </script> | ||||
|  | ||||
| @ -184,6 +184,20 @@ export const useCompanyStore = (useWindow = false) => { | ||||
|       setDefaultCurrency(data) { | ||||
|         this.defaultCurrency = data.currency | ||||
|       }, | ||||
|  | ||||
|       checkCompanyHasCurrencyTransactions() { | ||||
|         return new Promise((resolve, reject) => { | ||||
|           axios | ||||
|             .get(`/api/v1/company/has-transactions`) | ||||
|             .then((response) => { | ||||
|               resolve(response) | ||||
|             }) | ||||
|             .catch((err) => { | ||||
|               handleError(err) | ||||
|               reject(err) | ||||
|             }) | ||||
|         }) | ||||
|       }, | ||||
|     }, | ||||
|   })() | ||||
| } | ||||
|  | ||||
| @ -34,6 +34,24 @@ | ||||
|           /> | ||||
|         </BaseInputGroup> | ||||
|  | ||||
|         <BaseInputGroup | ||||
|           :label="$tc('wizard.company_slug')" | ||||
|           :help-text="$t('wizard.company_slug_help_text')" | ||||
|           :error=" | ||||
|             v$.companyForm.slug.$error && | ||||
|             v$.companyForm.slug.$errors[0].$message | ||||
|           " | ||||
|           required | ||||
|         > | ||||
|           <BaseInput | ||||
|             v-model="companyForm.slug" | ||||
|             :invalid="v$.companyForm.slug.$error" | ||||
|             type="text" | ||||
|             name="slug" | ||||
|             @input="v$.companyForm.slug.$touch()" | ||||
|           /> | ||||
|         </BaseInputGroup> | ||||
|  | ||||
|         <BaseInputGroup | ||||
|           :label="$t('wizard.country')" | ||||
|           :error=" | ||||
| @ -57,9 +75,7 @@ | ||||
|             track-by="name" | ||||
|           /> | ||||
|         </BaseInputGroup> | ||||
|       </div> | ||||
|  | ||||
|       <div class="grid grid-cols-1 gap-4 mb-4 md:grid-cols-2 md:mb-6"> | ||||
|         <BaseInputGroup :label="$t('wizard.state')"> | ||||
|           <BaseInput | ||||
|             v-model="companyForm.address.state" | ||||
| @ -144,9 +160,9 @@ | ||||
| </template> | ||||
|  | ||||
| <script setup> | ||||
| import { ref, computed, onMounted, reactive } from 'vue' | ||||
| import { ref, computed, onMounted, reactive, watch } from 'vue' | ||||
| import { useI18n } from 'vue-i18n' | ||||
| import { required, maxLength, helpers } from '@vuelidate/validators' | ||||
| import { required, minLength, maxLength, helpers } from '@vuelidate/validators' | ||||
| import { useVuelidate } from '@vuelidate/core' | ||||
| import { useGlobalStore } from '@/scripts/admin/stores/global' | ||||
| import { useCompanyStore } from '@/scripts/admin/stores/company' | ||||
| @ -162,6 +178,7 @@ let logoFileName = ref(null) | ||||
|  | ||||
| const companyForm = reactive({ | ||||
|   name: null, | ||||
|   slug: null, | ||||
|   address: { | ||||
|     address_street_1: '', | ||||
|     address_street_2: '', | ||||
| @ -188,10 +205,28 @@ onMounted(async () => { | ||||
|   })?.id | ||||
| }) | ||||
|  | ||||
| const slugValidator = (value) => { | ||||
|   return value == slugify(value) | ||||
| } | ||||
| const rules = { | ||||
|   companyForm: { | ||||
|     name: { | ||||
|       required: helpers.withMessage(t('validation.required'), required), | ||||
|       minLength: helpers.withMessage( | ||||
|         t('validation.name_min_length', { count: 3 }), | ||||
|         minLength(3) | ||||
|       ), | ||||
|     }, | ||||
|     slug: { | ||||
|       required: helpers.withMessage(t('validation.required'), required), | ||||
|       minLength: helpers.withMessage( | ||||
|         t('validation.name_min_length', { count: 3 }), | ||||
|         minLength(3) | ||||
|       ), | ||||
|       slugValidator: helpers.withMessage( | ||||
|         t('validation.invalid_slug'), | ||||
|         slugValidator | ||||
|       ), | ||||
|     }, | ||||
|     address: { | ||||
|       country_id: { | ||||
| @ -249,4 +284,24 @@ async function next() { | ||||
|     emit('next', 7) | ||||
|   } | ||||
| } | ||||
|  | ||||
| // watcher for if change company name then auto fill company slug value | ||||
| watch( | ||||
|   () => companyForm.name, | ||||
|   (currentValue) => { | ||||
|     companyForm.slug = slugify(currentValue) | ||||
|   } | ||||
| ) | ||||
|  | ||||
| function slugify(string) { | ||||
|   return string | ||||
|     .toString() | ||||
|     .trim() | ||||
|     .toLowerCase() | ||||
|     .replace(/\s+/g, '-') | ||||
|     .replace(/[^\w\-]+/g, '') | ||||
|     .replace(/\-\-+/g, '-') | ||||
|     .replace(/^-+/, '') | ||||
|     .replace(/-+$/, '') | ||||
| } | ||||
| </script> | ||||
|  | ||||
| @ -28,6 +28,19 @@ | ||||
|           /> | ||||
|         </BaseInputGroup> | ||||
|  | ||||
|         <BaseInputGroup | ||||
|           :label="$tc('settings.company_info.company_slug')" | ||||
|           :help-text="$t('settings.company_info.company_slug_help_text')" | ||||
|           :error="v$.slug.$error && v$.slug.$errors[0].$message" | ||||
|           required | ||||
|         > | ||||
|           <BaseInput | ||||
|             v-model="companyForm.slug" | ||||
|             :invalid="v$.slug.$error" | ||||
|             @blur="v$.slug.$touch()" | ||||
|           /> | ||||
|         </BaseInputGroup> | ||||
|  | ||||
|         <BaseInputGroup :label="$tc('settings.company_info.phone')"> | ||||
|           <BaseInput v-model="companyForm.address.phone" /> | ||||
|         </BaseInputGroup> | ||||
| @ -160,6 +173,7 @@ let isSaving = ref(false) | ||||
|  | ||||
| const companyForm = reactive({ | ||||
|   name: null, | ||||
|   slug: null, | ||||
|   logo: null, | ||||
|   address: { | ||||
|     address_street_1: '', | ||||
| @ -193,7 +207,14 @@ const rules = computed(() => { | ||||
|     name: { | ||||
|       required: helpers.withMessage(t('validation.required'), required), | ||||
|       minLength: helpers.withMessage( | ||||
|         t('validation.name_min_length'), | ||||
|         t('validation.name_min_length', { count: 3 }), | ||||
|         minLength(3) | ||||
|       ), | ||||
|     }, | ||||
|     slug: { | ||||
|       required: helpers.withMessage(t('validation.required'), required), | ||||
|       minLength: helpers.withMessage( | ||||
|         t('validation.name_min_length', { count: 3 }), | ||||
|         minLength(3) | ||||
|       ), | ||||
|     }, | ||||
|  | ||||
| @ -8,7 +8,11 @@ | ||||
|         <BaseInputGroup | ||||
|           :content-loading="isFetchingInitialData" | ||||
|           :label="$tc('settings.preferences.currency')" | ||||
|           :help-text="$t('settings.preferences.company_currency_unchangeable')" | ||||
|           :help-text=" | ||||
|             isCurrencyDisabled | ||||
|               ? $t('settings.preferences.company_currency_unchangeable') | ||||
|               : '' | ||||
|           " | ||||
|           :error="v$.currency.$error && v$.currency.$errors[0].$message" | ||||
|           required | ||||
|         > | ||||
| @ -21,7 +25,7 @@ | ||||
|             :searchable="true" | ||||
|             track-by="name" | ||||
|             :invalid="v$.currency.$error" | ||||
|             disabled | ||||
|             :disabled="isCurrencyDisabled" | ||||
|             class="w-full" | ||||
|           > | ||||
|           </BaseMultiselect> | ||||
| @ -187,6 +191,7 @@ const { t, tm } = useI18n() | ||||
| let isSaving = ref(false) | ||||
| let isDataSaving = ref(false) | ||||
| let isFetchingInitialData = ref(false) | ||||
| let isCurrencyDisabled = ref(true) | ||||
|  | ||||
| const settingsForm = reactive({ ...companyStore.selectedCompanySettings }) | ||||
|  | ||||
| @ -282,10 +287,14 @@ setInitialData() | ||||
| async function setInitialData() { | ||||
|   isFetchingInitialData.value = true | ||||
|   Promise.all([ | ||||
|     companyStore.checkCompanyHasCurrencyTransactions(), | ||||
|     globalStore.fetchCurrencies(), | ||||
|     globalStore.fetchDateFormats(), | ||||
|     globalStore.fetchTimeZones(), | ||||
|   ]).then(([res1]) => { | ||||
|     if (res1.data?.has_transactions == false) { | ||||
|       isCurrencyDisabled.value = false | ||||
|     } | ||||
|     isFetchingInitialData.value = false | ||||
|   }) | ||||
| } | ||||
|  | ||||
| @ -863,6 +863,8 @@ | ||||
|     "company_info": { | ||||
|       "company_info": "Company info", | ||||
|       "company_name": "Company Name", | ||||
|       "company_slug": "Company Slug", | ||||
|       "company_slug_help_text": "A unique URL friendly name for your company (It will appear on Customer Portal URL)", | ||||
|       "company_logo": "Company Logo", | ||||
|       "section_description": "Information about your company that will be displayed on invoices, estimates and other documents created by Crater.", | ||||
|       "phone": "Phone", | ||||
| @ -1324,6 +1326,8 @@ | ||||
|     "company_info": "Company Information", | ||||
|     "company_info_desc": "This information will be displayed on invoices. Note that you can edit this later on settings page.", | ||||
|     "company_name": "Company Name", | ||||
|     "company_slug": "Company Slug", | ||||
|     "company_slug_help_text": "A unique URL friendly name for your company (It will appear on Customer Portal URL)", | ||||
|     "company_logo": "Company Logo", | ||||
|     "logo_preview": "Logo Preview", | ||||
|     "preferences": "Company Preferences", | ||||
| @ -1454,7 +1458,8 @@ | ||||
|     "at_least_one_ability": "Please select atleast one Permission.", | ||||
|     "valid_driver_key": "Please enter a valid {driver} key.", | ||||
|     "valid_exchange_rate": "Please enter a valid exchange rate.", | ||||
|     "company_name_not_same": "Company name must match with given name." | ||||
|     "company_name_not_same": "Company name must match with given name.", | ||||
|     "invalid_slug": "Invalid Slug" | ||||
|   }, | ||||
|   "errors": { | ||||
|     "starter_plan": "This feature is available on Starter plan and onwards!", | ||||
|  | ||||
| @ -415,32 +415,31 @@ test('update estimate with EUR currency', function () { | ||||
|  | ||||
|     $response = putJson('api/v1/estimates/'.$estimate->id, $estimate2); | ||||
|  | ||||
|     $this->assertDatabaseHas('estimates', [ | ||||
|         'id' => $estimate['id'], | ||||
|         'template_name' => $estimate2['template_name'], | ||||
|         'estimate_number' => $estimate2['estimate_number'], | ||||
|         'discount_type' => $estimate2['discount_type'], | ||||
|         'discount_val' => $estimate2['discount_val'], | ||||
|         'sub_total' => $estimate2['sub_total'], | ||||
|         'discount' => $estimate2['discount'], | ||||
|         'customer_id' => $estimate2['customer_id'], | ||||
|         'total' => $estimate2['total'], | ||||
|         'tax' => $estimate2['tax'], | ||||
|         'exchange_rate' => $estimate2['exchange_rate'], | ||||
|         'base_discount_val' => $estimate2['base_discount_val'], | ||||
|         'base_sub_total' => $estimate2['base_sub_total'], | ||||
|         'base_total' => $estimate2['base_total'], | ||||
|         'base_tax' => $estimate2['base_tax'], | ||||
|     ]); | ||||
|     $estimate_assert = collect($estimate2) | ||||
|         ->only([ | ||||
|             'id', | ||||
|             'template_name', | ||||
|             'estimate_number', | ||||
|             'discount_type', | ||||
|             'discount_val', | ||||
|             'sub_total', | ||||
|             'discount', | ||||
|             'customer_id', | ||||
|             'total', | ||||
|             'tax' | ||||
|         ]) | ||||
|         ->toArray(); | ||||
|  | ||||
|     $this->assertDatabaseHas('estimate_items', [ | ||||
|         'estimate_id' => $estimate2['items'][0]['estimate_id'], | ||||
|         'exchange_rate' => $estimate2['items'][0]['exchange_rate'], | ||||
|         'base_price' => $estimate2['items'][0]['base_price'], | ||||
|         'base_discount_val' => $estimate2['items'][0]['base_discount_val'], | ||||
|         'base_tax' => $estimate2['items'][0]['base_tax'], | ||||
|         'base_total' => $estimate2['items'][0]['base_total'], | ||||
|     ]); | ||||
|     $this->assertDatabaseHas('estimates', $estimate_assert); | ||||
|  | ||||
|     $estimate_item_assert = collect($estimate2['items'][0]) | ||||
|         ->only([ | ||||
|             'estimate_id', | ||||
|             'amount' | ||||
|         ]) | ||||
|         ->toArray(); | ||||
|  | ||||
|     $this->assertDatabaseHas('estimate_items', $estimate_item_assert); | ||||
|  | ||||
|     $response->assertStatus(200); | ||||
| }); | ||||
|  | ||||
| @ -37,13 +37,15 @@ test('create expense', function () { | ||||
|  | ||||
|     postJson('api/v1/expenses', $expense)->assertStatus(201); | ||||
|  | ||||
|     $this->assertDatabaseHas('expenses', [ | ||||
|         'notes' => $expense['notes'], | ||||
|         'expense_category_id' => $expense['expense_category_id'], | ||||
|         'amount' => $expense['amount'], | ||||
|         'exchange_rate' => $expense['exchange_rate'], | ||||
|         'base_amount' => $expense['base_amount'], | ||||
|     ]); | ||||
|     $expense = collect($expense) | ||||
|         ->only([ | ||||
|             'notes', | ||||
|             'expense_category_id', | ||||
|             'amount' | ||||
|         ]) | ||||
|         ->toArray(); | ||||
|  | ||||
|     $this->assertDatabaseHas('expenses', $expense); | ||||
| }); | ||||
|  | ||||
| test('store validates using a form request', function () { | ||||
| @ -146,11 +148,13 @@ test('update expense with EUR currency', function () { | ||||
|  | ||||
|     putJson('api/v1/expenses/'.$expense->id, $expense2)->assertOk(); | ||||
|  | ||||
|     $this->assertDatabaseHas('expenses', [ | ||||
|         'id' => $expense->id, | ||||
|         'expense_category_id' => $expense2['expense_category_id'], | ||||
|         'amount' => $expense2['amount'], | ||||
|         'exchange_rate' => $expense2['exchange_rate'], | ||||
|         'base_amount' => $expense2['base_amount'], | ||||
|     ]); | ||||
|     $expense2 = collect($expense2) | ||||
|         ->only([ | ||||
|             'id', | ||||
|             'expense_category_id', | ||||
|             'amount' | ||||
|         ]) | ||||
|         ->toArray(); | ||||
|  | ||||
|     $this->assertDatabaseHas('expenses', $expense2); | ||||
| }); | ||||
|  | ||||
| @ -9,7 +9,6 @@ use Crater\Models\Tax; | ||||
| use Crater\Models\User; | ||||
| use Illuminate\Support\Facades\Artisan; | ||||
| use Laravel\Sanctum\Sanctum; | ||||
|  | ||||
| use function Pest\Laravel\getJson; | ||||
| use function Pest\Laravel\postJson; | ||||
| use function Pest\Laravel\putJson; | ||||
| @ -431,31 +430,36 @@ test('update invoice with EUR currency', function () { | ||||
|  | ||||
|     putJson('api/v1/invoices/'.$invoice->id, $invoice2)->assertOk(); | ||||
|  | ||||
|     $this->assertDatabaseHas('invoices', [ | ||||
|         'id' => $invoice['id'], | ||||
|         'invoice_number' => $invoice2['invoice_number'], | ||||
|         'sub_total' => $invoice2['sub_total'], | ||||
|         'total' => $invoice2['total'], | ||||
|         'tax' => $invoice2['tax'], | ||||
|         'discount' => $invoice2['discount'], | ||||
|         'customer_id' => $invoice2['customer_id'], | ||||
|         'template_name' => $invoice2['template_name'], | ||||
|         'exchange_rate' => $invoice2['exchange_rate'], | ||||
|         'base_total' => $invoice2['base_total'], | ||||
|     ]); | ||||
|     $invoice_assert = collect($invoice2) | ||||
|         ->only([ | ||||
|             'invoice_number', | ||||
|             'template_name', | ||||
|             'sub_total', | ||||
|             'total', | ||||
|             'tax', | ||||
|             'discount', | ||||
|             'customer_id', | ||||
|         ]) | ||||
|         ->toArray(); | ||||
|  | ||||
|     $this->assertDatabaseHas('invoice_items', [ | ||||
|         'invoice_id' => $invoice2['items'][0]['invoice_id'], | ||||
|         'item_id' => $invoice2['items'][0]['item_id'], | ||||
|         'name' => $invoice2['items'][0]['name'], | ||||
|         'exchange_rate' => $invoice2['items'][0]['exchange_rate'], | ||||
|         'base_price' => $invoice2['items'][0]['base_price'], | ||||
|         'base_total' => $invoice2['items'][0]['base_total'], | ||||
|     ]); | ||||
|     $this->assertDatabaseHas('invoices', $invoice_assert); | ||||
|  | ||||
|     $this->assertDatabaseHas('taxes', [ | ||||
|         'amount' => $invoice2['taxes'][0]['amount'], | ||||
|         'name' => $invoice2['taxes'][0]['name'], | ||||
|         'base_amount' => $invoice2['taxes'][0]['base_amount'], | ||||
|     ]); | ||||
|     $invoice_item_assert = collect($invoice2['items'][0]) | ||||
|         ->only([ | ||||
|             'invoice_id', | ||||
|             'item_id', | ||||
|             'name', | ||||
|         ]) | ||||
|         ->toArray(); | ||||
|  | ||||
|     $this->assertDatabaseHas('invoice_items', $invoice_item_assert); | ||||
|  | ||||
|     $invoice_tax_assert = collect($invoice2['taxes'][0]) | ||||
|         ->only([ | ||||
|             'name', | ||||
|             'amount' | ||||
|         ]) | ||||
|         ->toArray(); | ||||
|  | ||||
|     $this->assertDatabaseHas('taxes', $invoice_tax_assert); | ||||
| }); | ||||
|  | ||||
		Reference in New Issue
	
	Block a user