瀏覽代碼

Merge pull request #149 from andrewdwallo/development-3.x

Development 3.x
3.x
Andrew Wallo 5 月之前
父節點
當前提交
c2982c842c
沒有連結到貢獻者的電子郵件帳戶。

+ 5
- 3
app/Services/ChartOfAccountsService.php 查看文件

9
 use App\Models\Accounting\Adjustment;
9
 use App\Models\Accounting\Adjustment;
10
 use App\Models\Banking\BankAccount;
10
 use App\Models\Banking\BankAccount;
11
 use App\Models\Company;
11
 use App\Models\Company;
12
-use App\Utilities\Currency\CurrencyAccessor;
13
 use Exception;
12
 use Exception;
14
 
13
 
15
 class ChartOfAccountsService
14
 class ChartOfAccountsService
16
 {
15
 {
17
-    public function createChartOfAccounts(Company $company): void
16
+    private string $currencyCode;
17
+
18
+    public function createChartOfAccounts(Company $company, string $currencyCode = 'USD'): void
18
     {
19
     {
20
+        $this->currencyCode = $currencyCode;
19
         $chartOfAccounts = config('chart-of-accounts.default');
21
         $chartOfAccounts = config('chart-of-accounts.default');
20
 
22
 
21
         // Always create a non-recoverable "Purchase Tax" adjustment, even without an account
23
         // Always create a non-recoverable "Purchase Tax" adjustment, even without an account
53
     {
55
     {
54
         if (isset($subtypeConfig['accounts']) && is_array($subtypeConfig['accounts'])) {
56
         if (isset($subtypeConfig['accounts']) && is_array($subtypeConfig['accounts'])) {
55
             $baseCode = $subtypeConfig['base_code'];
57
             $baseCode = $subtypeConfig['base_code'];
56
-            $defaultCurrencyCode = CurrencyAccessor::getDefaultCurrency();
58
+            $defaultCurrencyCode = $this->currencyCode;
57
 
59
 
58
             if (empty($defaultCurrencyCode)) {
60
             if (empty($defaultCurrencyCode)) {
59
                 throw new Exception('No default currency available for creating accounts.');
61
                 throw new Exception('No default currency available for creating accounts.');

+ 1
- 1
app/Services/CompanyDefaultService.php 查看文件

17
 
17
 
18
             // Create Chart of Accounts
18
             // Create Chart of Accounts
19
             $chartOfAccountsService = app(ChartOfAccountsService::class);
19
             $chartOfAccountsService = app(ChartOfAccountsService::class);
20
-            $chartOfAccountsService->createChartOfAccounts($company);
20
+            $chartOfAccountsService->createChartOfAccounts($company, $currencyCode);
21
 
21
 
22
             // Get the default bank account and update the company default record
22
             // Get the default bank account and update the company default record
23
             $defaultBankAccount = $company->bankAccounts()->where('enabled', true)->firstOrFail();
23
             $defaultBankAccount = $company->bankAccounts()->where('enabled', true)->firstOrFail();

+ 9
- 2
database/factories/Accounting/BillFactory.php 查看文件

8
 use App\Models\Accounting\DocumentLineItem;
8
 use App\Models\Accounting\DocumentLineItem;
9
 use App\Models\Banking\BankAccount;
9
 use App\Models\Banking\BankAccount;
10
 use App\Models\Common\Vendor;
10
 use App\Models\Common\Vendor;
11
+use App\Models\Company;
11
 use App\Models\Setting\DocumentDefault;
12
 use App\Models\Setting\DocumentDefault;
12
 use App\Utilities\Currency\CurrencyConverter;
13
 use App\Utilities\Currency\CurrencyConverter;
13
 use Illuminate\Database\Eloquent\Factories\Factory;
14
 use Illuminate\Database\Eloquent\Factories\Factory;
42
 
43
 
43
         return [
44
         return [
44
             'company_id' => 1,
45
             'company_id' => 1,
45
-            'vendor_id' => Vendor::inRandomOrder()->value('id'),
46
+            'vendor_id' => fn (array $attributes) => Vendor::where('company_id', $attributes['company_id'])->inRandomOrder()->value('id'),
46
             'bill_number' => $this->faker->unique()->numerify('BILL-####'),
47
             'bill_number' => $this->faker->unique()->numerify('BILL-####'),
47
             'order_number' => $this->faker->unique()->numerify('PO-####'),
48
             'order_number' => $this->faker->unique()->numerify('PO-####'),
48
             'date' => $billDate,
49
             'date' => $billDate,
49
             'due_date' => Carbon::parse($billDate)->addDays($dueDays),
50
             'due_date' => Carbon::parse($billDate)->addDays($dueDays),
50
             'status' => BillStatus::Open,
51
             'status' => BillStatus::Open,
51
-            'currency_code' => 'USD',
52
+            'currency_code' => function (array $attributes) {
53
+                $vendor = Vendor::find($attributes['vendor_id']);
54
+
55
+                return $vendor->currency_code ??
56
+                    Company::find($attributes['company_id'])->default->currency_code ??
57
+                    'USD';
58
+            },
52
             'notes' => $this->faker->sentence,
59
             'notes' => $this->faker->sentence,
53
             'created_by' => 1,
60
             'created_by' => 1,
54
             'updated_by' => 1,
61
             'updated_by' => 1,

+ 30
- 36
database/factories/Accounting/DocumentLineItemFactory.php 查看文件

42
     {
42
     {
43
         return $this
43
         return $this
44
             ->for($invoice, 'documentable')
44
             ->for($invoice, 'documentable')
45
-            ->state(function (array $attributes) {
46
-                $offering = Offering::where('sellable', true)
45
+            ->for($invoice->company, 'company')
46
+            ->afterCreating(function (DocumentLineItem $lineItem) {
47
+                $offering = Offering::query()
48
+                    ->where('company_id', $lineItem->company_id)
49
+                    ->where('sellable', true)
47
                     ->inRandomOrder()
50
                     ->inRandomOrder()
48
-                    ->first();
51
+                    ->firstOrFail();
49
 
52
 
50
-                return [
53
+                $lineItem->updateQuietly([
51
                     'offering_id' => $offering->id,
54
                     'offering_id' => $offering->id,
52
                     'unit_price' => $offering->price,
55
                     'unit_price' => $offering->price,
53
-                ];
54
-            })
55
-            ->afterCreating(function (DocumentLineItem $lineItem) {
56
-                $offering = $lineItem->offering;
56
+                ]);
57
 
57
 
58
-                if ($offering) {
59
-                    $lineItem->salesTaxes()->syncWithoutDetaching($offering->salesTaxes->pluck('id')->toArray());
60
-                    $lineItem->salesDiscounts()->syncWithoutDetaching($offering->salesDiscounts->pluck('id')->toArray());
61
-                }
58
+                $lineItem->salesTaxes()->syncWithoutDetaching($offering->salesTaxes->pluck('id')->toArray());
59
+                $lineItem->salesDiscounts()->syncWithoutDetaching($offering->salesDiscounts->pluck('id')->toArray());
62
 
60
 
63
                 $lineItem->refresh();
61
                 $lineItem->refresh();
64
 
62
 
76
     {
74
     {
77
         return $this
75
         return $this
78
             ->for($estimate, 'documentable')
76
             ->for($estimate, 'documentable')
79
-            ->state(function (array $attributes) {
80
-                $offering = Offering::where('sellable', true)
77
+            ->for($estimate->company, 'company')
78
+            ->afterCreating(function (DocumentLineItem $lineItem) {
79
+                $offering = Offering::query()
80
+                    ->where('company_id', $lineItem->company_id)
81
+                    ->where('sellable', true)
81
                     ->inRandomOrder()
82
                     ->inRandomOrder()
82
-                    ->first();
83
+                    ->firstOrFail();
83
 
84
 
84
-                return [
85
+                $lineItem->updateQuietly([
85
                     'offering_id' => $offering->id,
86
                     'offering_id' => $offering->id,
86
                     'unit_price' => $offering->price,
87
                     'unit_price' => $offering->price,
87
-                ];
88
-            })
89
-            ->afterCreating(function (DocumentLineItem $lineItem) {
90
-                $offering = $lineItem->offering;
88
+                ]);
91
 
89
 
92
-                if ($offering) {
93
-                    $lineItem->salesTaxes()->syncWithoutDetaching($offering->salesTaxes->pluck('id')->toArray());
94
-                    $lineItem->salesDiscounts()->syncWithoutDetaching($offering->salesDiscounts->pluck('id')->toArray());
95
-                }
90
+                $lineItem->salesTaxes()->syncWithoutDetaching($offering->salesTaxes->pluck('id')->toArray());
91
+                $lineItem->salesDiscounts()->syncWithoutDetaching($offering->salesDiscounts->pluck('id')->toArray());
96
 
92
 
97
                 $lineItem->refresh();
93
                 $lineItem->refresh();
98
 
94
 
110
     {
106
     {
111
         return $this
107
         return $this
112
             ->for($bill, 'documentable')
108
             ->for($bill, 'documentable')
113
-            ->state(function (array $attributes) {
114
-                $offering = Offering::where('purchasable', true)
109
+            ->for($bill->company, 'company')
110
+            ->afterCreating(function (DocumentLineItem $lineItem) {
111
+                $offering = Offering::query()
112
+                    ->where('company_id', $lineItem->company_id)
113
+                    ->where('purchasable', true)
115
                     ->inRandomOrder()
114
                     ->inRandomOrder()
116
-                    ->first();
115
+                    ->firstOrFail();
117
 
116
 
118
-                return [
117
+                $lineItem->updateQuietly([
119
                     'offering_id' => $offering->id,
118
                     'offering_id' => $offering->id,
120
                     'unit_price' => $offering->price,
119
                     'unit_price' => $offering->price,
121
-                ];
122
-            })
123
-            ->afterCreating(function (DocumentLineItem $lineItem) {
124
-                $offering = $lineItem->offering;
120
+                ]);
125
 
121
 
126
-                if ($offering) {
127
-                    $lineItem->purchaseTaxes()->syncWithoutDetaching($offering->purchaseTaxes->pluck('id')->toArray());
128
-                    $lineItem->purchaseDiscounts()->syncWithoutDetaching($offering->purchaseDiscounts->pluck('id')->toArray());
129
-                }
122
+                $lineItem->purchaseTaxes()->syncWithoutDetaching($offering->purchaseTaxes->pluck('id')->toArray());
123
+                $lineItem->purchaseDiscounts()->syncWithoutDetaching($offering->purchaseDiscounts->pluck('id')->toArray());
130
 
124
 
131
                 $lineItem->refresh();
125
                 $lineItem->refresh();
132
 
126
 

+ 9
- 2
database/factories/Accounting/EstimateFactory.php 查看文件

6
 use App\Models\Accounting\DocumentLineItem;
6
 use App\Models\Accounting\DocumentLineItem;
7
 use App\Models\Accounting\Estimate;
7
 use App\Models\Accounting\Estimate;
8
 use App\Models\Common\Client;
8
 use App\Models\Common\Client;
9
+use App\Models\Company;
9
 use App\Models\Setting\DocumentDefault;
10
 use App\Models\Setting\DocumentDefault;
10
 use Illuminate\Database\Eloquent\Factories\Factory;
11
 use Illuminate\Database\Eloquent\Factories\Factory;
11
 use Illuminate\Support\Carbon;
12
 use Illuminate\Support\Carbon;
31
 
32
 
32
         return [
33
         return [
33
             'company_id' => 1,
34
             'company_id' => 1,
34
-            'client_id' => Client::inRandomOrder()->value('id'),
35
+            'client_id' => fn (array $attributes) => Client::where('company_id', $attributes['company_id'])->inRandomOrder()->value('id'),
35
             'header' => 'Estimate',
36
             'header' => 'Estimate',
36
             'subheader' => 'Estimate',
37
             'subheader' => 'Estimate',
37
             'estimate_number' => $this->faker->unique()->numerify('EST-####'),
38
             'estimate_number' => $this->faker->unique()->numerify('EST-####'),
39
             'date' => $estimateDate,
40
             'date' => $estimateDate,
40
             'expiration_date' => Carbon::parse($estimateDate)->addDays($this->faker->numberBetween(14, 30)),
41
             'expiration_date' => Carbon::parse($estimateDate)->addDays($this->faker->numberBetween(14, 30)),
41
             'status' => EstimateStatus::Draft,
42
             'status' => EstimateStatus::Draft,
42
-            'currency_code' => 'USD',
43
+            'currency_code' => function (array $attributes) {
44
+                $client = Client::find($attributes['client_id']);
45
+
46
+                return $client->currency_code ??
47
+                    Company::find($attributes['company_id'])->default->currency_code ??
48
+                    'USD';
49
+            },
43
             'terms' => $this->faker->sentence,
50
             'terms' => $this->faker->sentence,
44
             'footer' => $this->faker->sentence,
51
             'footer' => $this->faker->sentence,
45
             'created_by' => 1,
52
             'created_by' => 1,

+ 9
- 2
database/factories/Accounting/InvoiceFactory.php 查看文件

8
 use App\Models\Accounting\Invoice;
8
 use App\Models\Accounting\Invoice;
9
 use App\Models\Banking\BankAccount;
9
 use App\Models\Banking\BankAccount;
10
 use App\Models\Common\Client;
10
 use App\Models\Common\Client;
11
+use App\Models\Company;
11
 use App\Models\Setting\DocumentDefault;
12
 use App\Models\Setting\DocumentDefault;
12
 use App\Utilities\Currency\CurrencyConverter;
13
 use App\Utilities\Currency\CurrencyConverter;
13
 use Illuminate\Database\Eloquent\Factories\Factory;
14
 use Illuminate\Database\Eloquent\Factories\Factory;
34
 
35
 
35
         return [
36
         return [
36
             'company_id' => 1,
37
             'company_id' => 1,
37
-            'client_id' => Client::inRandomOrder()->value('id'),
38
+            'client_id' => fn (array $attributes) => Client::where('company_id', $attributes['company_id'])->inRandomOrder()->value('id'),
38
             'header' => 'Invoice',
39
             'header' => 'Invoice',
39
             'subheader' => 'Invoice',
40
             'subheader' => 'Invoice',
40
             'invoice_number' => $this->faker->unique()->numerify('INV-####'),
41
             'invoice_number' => $this->faker->unique()->numerify('INV-####'),
42
             'date' => $invoiceDate,
43
             'date' => $invoiceDate,
43
             'due_date' => Carbon::parse($invoiceDate)->addDays($this->faker->numberBetween(14, 60)),
44
             'due_date' => Carbon::parse($invoiceDate)->addDays($this->faker->numberBetween(14, 60)),
44
             'status' => InvoiceStatus::Draft,
45
             'status' => InvoiceStatus::Draft,
45
-            'currency_code' => 'USD',
46
+            'currency_code' => function (array $attributes) {
47
+                $client = Client::find($attributes['client_id']);
48
+
49
+                return $client->currency_code ??
50
+                    Company::find($attributes['company_id'])->default->currency_code ??
51
+                    'USD';
52
+            },
46
             'terms' => $this->faker->sentence,
53
             'terms' => $this->faker->sentence,
47
             'footer' => $this->faker->sentence,
54
             'footer' => $this->faker->sentence,
48
             'created_by' => 1,
55
             'created_by' => 1,

+ 9
- 2
database/factories/Accounting/RecurringInvoiceFactory.php 查看文件

13
 use App\Models\Accounting\DocumentLineItem;
13
 use App\Models\Accounting\DocumentLineItem;
14
 use App\Models\Accounting\RecurringInvoice;
14
 use App\Models\Accounting\RecurringInvoice;
15
 use App\Models\Common\Client;
15
 use App\Models\Common\Client;
16
+use App\Models\Company;
16
 use Illuminate\Database\Eloquent\Factories\Factory;
17
 use Illuminate\Database\Eloquent\Factories\Factory;
17
 use Illuminate\Support\Carbon;
18
 use Illuminate\Support\Carbon;
18
 
19
 
35
     {
36
     {
36
         return [
37
         return [
37
             'company_id' => 1,
38
             'company_id' => 1,
38
-            'client_id' => Client::inRandomOrder()->value('id'),
39
+            'client_id' => fn (array $attributes) => Client::where('company_id', $attributes['company_id'])->inRandomOrder()->value('id'),
39
             'header' => 'Invoice',
40
             'header' => 'Invoice',
40
             'subheader' => 'Invoice',
41
             'subheader' => 'Invoice',
41
             'order_number' => $this->faker->unique()->numerify('ORD-####'),
42
             'order_number' => $this->faker->unique()->numerify('ORD-####'),
42
             'payment_terms' => PaymentTerms::Net30,
43
             'payment_terms' => PaymentTerms::Net30,
43
             'status' => RecurringInvoiceStatus::Draft,
44
             'status' => RecurringInvoiceStatus::Draft,
44
-            'currency_code' => 'USD',
45
+            'currency_code' => function (array $attributes) {
46
+                $client = Client::find($attributes['client_id']);
47
+
48
+                return $client->currency_code ??
49
+                    Company::find($attributes['company_id'])->default->currency_code ??
50
+                    'USD';
51
+            },
45
             'terms' => $this->faker->sentence,
52
             'terms' => $this->faker->sentence,
46
             'footer' => $this->faker->sentence,
53
             'footer' => $this->faker->sentence,
47
             'created_by' => 1,
54
             'created_by' => 1,

+ 11
- 0
database/factories/Common/AddressFactory.php 查看文件

4
 
4
 
5
 use App\Enums\Common\AddressType;
5
 use App\Enums\Common\AddressType;
6
 use App\Models\Common\Address;
6
 use App\Models\Common\Address;
7
+use Database\Factories\Concerns\HasParentRelationship;
7
 use Illuminate\Database\Eloquent\Factories\Factory;
8
 use Illuminate\Database\Eloquent\Factories\Factory;
8
 
9
 
9
 /**
10
 /**
11
  */
12
  */
12
 class AddressFactory extends Factory
13
 class AddressFactory extends Factory
13
 {
14
 {
15
+    use HasParentRelationship;
16
+
14
     /**
17
     /**
15
      * The name of the factory's corresponding model.
18
      * The name of the factory's corresponding model.
16
      */
19
      */
60
             'type' => AddressType::General,
63
             'type' => AddressType::General,
61
         ]);
64
         ]);
62
     }
65
     }
66
+
67
+    public function forCountry(string $countryCode): self
68
+    {
69
+        return $this->state([
70
+            'state_id' => $this->faker->state($countryCode),
71
+            'country_code' => $countryCode,
72
+        ]);
73
+    }
63
 }
74
 }

+ 14
- 5
database/factories/Common/ClientFactory.php 查看文件

5
 use App\Models\Common\Address;
5
 use App\Models\Common\Address;
6
 use App\Models\Common\Client;
6
 use App\Models\Common\Client;
7
 use App\Models\Common\Contact;
7
 use App\Models\Common\Contact;
8
+use App\Models\Company;
8
 use Illuminate\Database\Eloquent\Factories\Factory;
9
 use Illuminate\Database\Eloquent\Factories\Factory;
9
 
10
 
10
 /**
11
 /**
27
         return [
28
         return [
28
             'company_id' => 1,
29
             'company_id' => 1,
29
             'name' => $this->faker->company,
30
             'name' => $this->faker->company,
30
-            'currency_code' => 'USD',
31
+            'currency_code' => fn (array $attributes) => Company::find($attributes['company_id'])->default->currency_code ?? 'USD',
31
             'account_number' => $this->faker->unique()->numerify(str_repeat('#', 12)),
32
             'account_number' => $this->faker->unique()->numerify(str_repeat('#', 12)),
32
             'website' => $this->faker->url,
33
             'website' => $this->faker->url,
33
             'notes' => $this->faker->sentence,
34
             'notes' => $this->faker->sentence,
38
 
39
 
39
     public function withContacts(int $count = 1): self
40
     public function withContacts(int $count = 1): self
40
     {
41
     {
41
-        return $this->has(Contact::factory()->count($count));
42
+        return $this->has(
43
+            Contact::factory()
44
+                ->count($count)
45
+                ->useParentCompany()
46
+        );
42
     }
47
     }
43
 
48
 
44
     public function withPrimaryContact(): self
49
     public function withPrimaryContact(): self
45
     {
50
     {
46
-        return $this->has(Contact::factory()->primary());
51
+        return $this->has(
52
+            Contact::factory()
53
+                ->primary()
54
+                ->useParentCompany()
55
+        );
47
     }
56
     }
48
 
57
 
49
     public function withAddresses(): self
58
     public function withAddresses(): self
50
     {
59
     {
51
         return $this
60
         return $this
52
-            ->has(Address::factory()->billing())
53
-            ->has(Address::factory()->shipping());
61
+            ->has(Address::factory()->billing()->useParentCompany())
62
+            ->has(Address::factory()->shipping()->useParentCompany());
54
     }
63
     }
55
 }
64
 }

+ 3
- 0
database/factories/Common/ContactFactory.php 查看文件

3
 namespace Database\Factories\Common;
3
 namespace Database\Factories\Common;
4
 
4
 
5
 use App\Models\Common\Contact;
5
 use App\Models\Common\Contact;
6
+use Database\Factories\Concerns\HasParentRelationship;
6
 use Illuminate\Database\Eloquent\Factories\Factory;
7
 use Illuminate\Database\Eloquent\Factories\Factory;
7
 
8
 
8
 /**
9
 /**
10
  */
11
  */
11
 class ContactFactory extends Factory
12
 class ContactFactory extends Factory
12
 {
13
 {
14
+    use HasParentRelationship;
15
+
13
     /**
16
     /**
14
      * The name of the factory's corresponding model.
17
      * The name of the factory's corresponding model.
15
      */
18
      */

+ 41
- 57
database/factories/Common/OfferingFactory.php 查看文件

34
             'description' => $this->faker->sentence,
34
             'description' => $this->faker->sentence,
35
             'type' => $this->faker->randomElement(OfferingType::cases()),
35
             'type' => $this->faker->randomElement(OfferingType::cases()),
36
             'price' => $this->faker->numberBetween(5, 1000),
36
             'price' => $this->faker->numberBetween(5, 1000),
37
-            'sellable' => $this->faker->boolean(80),
38
-            'purchasable' => $this->faker->boolean(80),
39
-            'income_account_id' => function (array $attributes) {
40
-                return $attributes['sellable'] ? 10 : null;
41
-            },
42
-            'expense_account_id' => function (array $attributes) {
43
-                return $attributes['purchasable'] ? $this->faker->numberBetween(17, 35) : null;
44
-            },
37
+            'sellable' => false,
38
+            'purchasable' => false,
39
+            'income_account_id' => null,
40
+            'expense_account_id' => null,
45
             'created_by' => 1,
41
             'created_by' => 1,
46
             'updated_by' => 1,
42
             'updated_by' => 1,
47
         ];
43
         ];
48
     }
44
     }
49
 
45
 
50
-    public function sellable(): self
46
+    public function withSalesAdjustments(): self
51
     {
47
     {
52
-        $incomeAccount = Account::query()
53
-            ->where('category', AccountCategory::Revenue)
54
-            ->where('type', AccountType::OperatingRevenue)
55
-            ->inRandomOrder()
56
-            ->first();
48
+        return $this->afterCreating(function (Offering $offering) {
49
+            $incomeAccount = Account::query()
50
+                ->where('company_id', $offering->company_id)
51
+                ->where('category', AccountCategory::Revenue)
52
+                ->where('type', AccountType::OperatingRevenue)
53
+                ->inRandomOrder()
54
+                ->firstOrFail();
57
 
55
 
58
-        return $this->state(function (array $attributes) use ($incomeAccount) {
59
-            return [
56
+            $offering->updateQuietly([
60
                 'sellable' => true,
57
                 'sellable' => true,
61
-                'income_account_id' => $incomeAccount?->id ?? 10,
62
-            ];
63
-        });
64
-    }
65
-
66
-    public function purchasable(): self
67
-    {
68
-        $expenseAccount = Account::query()
69
-            ->where('category', AccountCategory::Expense)
70
-            ->where('type', AccountType::OperatingExpense)
71
-            ->inRandomOrder()
72
-            ->first();
73
-
74
-        return $this->state(function (array $attributes) use ($expenseAccount) {
75
-            return [
76
-                'purchasable' => true,
77
-                'expense_account_id' => $expenseAccount?->id ?? $this->faker->numberBetween(17, 35),
78
-            ];
79
-        });
80
-    }
58
+                'income_account_id' => $incomeAccount->id,
59
+            ]);
81
 
60
 
82
-    public function withSalesAdjustments(): self
83
-    {
84
-        return $this->afterCreating(function (Offering $offering) {
85
-            if ($offering->sellable) {
86
-                $adjustments = $offering->company?->adjustments()
87
-                    ->where('type', AdjustmentType::Sales)
88
-                    ->pluck('id');
61
+            $adjustments = $offering->company?->adjustments()
62
+                ->where('type', AdjustmentType::Sales)
63
+                ->pluck('id');
89
 
64
 
90
-                $adjustmentsToAttach = $adjustments->isNotEmpty()
91
-                    ? $adjustments->random(min(2, $adjustments->count()))
92
-                    : Adjustment::factory()->salesTax()->count(2)->create()->pluck('id');
65
+            $adjustmentsToAttach = $adjustments->isNotEmpty()
66
+                ? $adjustments->random(min(2, $adjustments->count()))
67
+                : Adjustment::factory()->salesTax()->count(2)->create()->pluck('id');
93
 
68
 
94
-                $offering->salesAdjustments()->attach($adjustmentsToAttach);
95
-            }
69
+            $offering->salesAdjustments()->attach($adjustmentsToAttach);
96
         });
70
         });
97
     }
71
     }
98
 
72
 
99
     public function withPurchaseAdjustments(): self
73
     public function withPurchaseAdjustments(): self
100
     {
74
     {
101
         return $this->afterCreating(function (Offering $offering) {
75
         return $this->afterCreating(function (Offering $offering) {
102
-            if ($offering->purchasable) {
103
-                $adjustments = $offering->company?->adjustments()
104
-                    ->where('type', AdjustmentType::Purchase)
105
-                    ->pluck('id');
76
+            $expenseAccount = Account::query()
77
+                ->where('company_id', $offering->company_id)
78
+                ->where('category', AccountCategory::Expense)
79
+                ->where('type', AccountType::OperatingExpense)
80
+                ->inRandomOrder()
81
+                ->firstOrFail();
82
+
83
+            $offering->updateQuietly([
84
+                'purchasable' => true,
85
+                'expense_account_id' => $expenseAccount->id,
86
+            ]);
87
+
88
+            $adjustments = $offering->company?->adjustments()
89
+                ->where('type', AdjustmentType::Purchase)
90
+                ->pluck('id');
106
 
91
 
107
-                $adjustmentsToAttach = $adjustments->isNotEmpty()
108
-                    ? $adjustments->random(min(2, $adjustments->count()))
109
-                    : Adjustment::factory()->purchaseTax()->count(2)->create()->pluck('id');
92
+            $adjustmentsToAttach = $adjustments->isNotEmpty()
93
+                ? $adjustments->random(min(2, $adjustments->count()))
94
+                : Adjustment::factory()->purchaseTax()->count(2)->create()->pluck('id');
110
 
95
 
111
-                $offering->purchaseAdjustments()->attach($adjustmentsToAttach);
112
-            }
96
+            $offering->purchaseAdjustments()->attach($adjustmentsToAttach);
113
         });
97
         });
114
     }
98
     }
115
 }
99
 }

+ 4
- 3
database/factories/Common/VendorFactory.php 查看文件

7
 use App\Models\Common\Address;
7
 use App\Models\Common\Address;
8
 use App\Models\Common\Contact;
8
 use App\Models\Common\Contact;
9
 use App\Models\Common\Vendor;
9
 use App\Models\Common\Vendor;
10
+use App\Models\Company;
10
 use Illuminate\Database\Eloquent\Factories\Factory;
11
 use Illuminate\Database\Eloquent\Factories\Factory;
11
 
12
 
12
 /**
13
 /**
39
             'ein' => function (array $attributes) {
40
             'ein' => function (array $attributes) {
40
                 return $attributes['contractor_type'] === ContractorType::Business ? $this->faker->numerify(str_repeat('#', 9)) : null;
41
                 return $attributes['contractor_type'] === ContractorType::Business ? $this->faker->numerify(str_repeat('#', 9)) : null;
41
             },
42
             },
42
-            'currency_code' => 'USD',
43
+            'currency_code' => fn (array $attributes) => Company::find($attributes['company_id'])->default->currency_code ?? 'USD',
43
             'account_number' => $this->faker->unique()->numerify(str_repeat('#', 12)),
44
             'account_number' => $this->faker->unique()->numerify(str_repeat('#', 12)),
44
             'website' => $this->faker->url,
45
             'website' => $this->faker->url,
45
             'notes' => $this->faker->sentence,
46
             'notes' => $this->faker->sentence,
80
 
81
 
81
     public function withContact(): self
82
     public function withContact(): self
82
     {
83
     {
83
-        return $this->has(Contact::factory()->primary());
84
+        return $this->has(Contact::factory()->primary()->useParentCompany());
84
     }
85
     }
85
 
86
 
86
     public function withAddress(): self
87
     public function withAddress(): self
87
     {
88
     {
88
-        return $this->has(Address::factory()->general());
89
+        return $this->has(Address::factory()->general()->useParentCompany());
89
     }
90
     }
90
 }
91
 }

+ 31
- 10
database/factories/CompanyFactory.php 查看文件

39
         ];
39
         ];
40
     }
40
     }
41
 
41
 
42
-    public function withCompanyProfile(): self
42
+    public function withCompanyProfile(?string $countryCode = null): self
43
     {
43
     {
44
-        return $this->afterCreating(function (Company $company) {
45
-            CompanyProfile::factory()->forCompany($company)->withAddress()->create();
44
+        return $this->afterCreating(function (Company $company) use ($countryCode) {
45
+            CompanyProfile::factory()
46
+                ->forCompany($company)
47
+                ->withAddress($countryCode)
48
+                ->create();
46
         });
49
         });
47
     }
50
     }
48
 
51
 
49
     /**
52
     /**
50
      * Set up default settings for the company after creation.
53
      * Set up default settings for the company after creation.
51
      */
54
      */
52
-    public function withCompanyDefaults(): self
55
+    public function withCompanyDefaults(string $currencyCode = 'USD', string $locale = 'en'): self
53
     {
56
     {
54
-        return $this->afterCreating(function (Company $company) {
57
+        return $this->afterCreating(function (Company $company) use ($currencyCode, $locale) {
55
             $countryCode = $company->profile->address->country_code;
58
             $countryCode = $company->profile->address->country_code;
56
             $companyDefaultService = app(CompanyDefaultService::class);
59
             $companyDefaultService = app(CompanyDefaultService::class);
57
-            $companyDefaultService->createCompanyDefaults($company, $company->owner, 'USD', $countryCode, 'en');
60
+            $companyDefaultService->createCompanyDefaults($company, $company->owner, $currencyCode, $countryCode, $locale);
58
         });
61
         });
59
     }
62
     }
60
 
63
 
75
 
78
 
76
     public function withClients(int $count = 10): self
79
     public function withClients(int $count = 10): self
77
     {
80
     {
78
-        return $this->has(Client::factory()->count($count)->withPrimaryContact()->withAddresses());
81
+        return $this->afterCreating(function (Company $company) use ($count) {
82
+            Client::factory()
83
+                ->count($count)
84
+                ->withPrimaryContact()
85
+                ->withAddresses()
86
+                ->create([
87
+                    'company_id' => $company->id,
88
+                    'created_by' => $company->user_id,
89
+                    'updated_by' => $company->user_id,
90
+                ]);
91
+        });
79
     }
92
     }
80
 
93
 
81
     public function withVendors(int $count = 10): self
94
     public function withVendors(int $count = 10): self
82
     {
95
     {
83
-        return $this->has(Vendor::factory()->count($count)->withContact()->withAddress());
96
+        return $this->afterCreating(function (Company $company) use ($count) {
97
+            Vendor::factory()
98
+                ->count($count)
99
+                ->withContact()
100
+                ->withAddress()
101
+                ->create([
102
+                    'company_id' => $company->id,
103
+                    'created_by' => $company->user_id,
104
+                    'updated_by' => $company->user_id,
105
+                ]);
106
+        });
84
     }
107
     }
85
 
108
 
86
     public function withOfferings(int $count = 10): self
109
     public function withOfferings(int $count = 10): self
88
         return $this->afterCreating(function (Company $company) use ($count) {
111
         return $this->afterCreating(function (Company $company) use ($count) {
89
             Offering::factory()
112
             Offering::factory()
90
                 ->count($count)
113
                 ->count($count)
91
-                ->sellable()
92
                 ->withSalesAdjustments()
114
                 ->withSalesAdjustments()
93
-                ->purchasable()
94
                 ->withPurchaseAdjustments()
115
                 ->withPurchaseAdjustments()
95
                 ->create([
116
                 ->create([
96
                     'company_id' => $company->id,
117
                     'company_id' => $company->id,

+ 19
- 0
database/factories/Concerns/HasParentRelationship.php 查看文件

1
+<?php
2
+
3
+namespace Database\Factories\Concerns;
4
+
5
+use Illuminate\Database\Eloquent\Model;
6
+
7
+trait HasParentRelationship
8
+{
9
+    public function useParentCompany(): self
10
+    {
11
+        return $this->state(function (array $attributes, Model $parent) {
12
+            return [
13
+                'company_id' => $parent->company_id,
14
+                'created_by' => $parent->created_by ?? 1,
15
+                'updated_by' => $parent->updated_by ?? 1,
16
+            ];
17
+        });
18
+    }
19
+}

+ 9
- 2
database/factories/Setting/CompanyProfileFactory.php 查看文件

42
         ]);
42
         ]);
43
     }
43
     }
44
 
44
 
45
-    public function withAddress(): self
45
+    public function withAddress(?string $countryCode = null): self
46
     {
46
     {
47
-        return $this->has(Address::factory()->general());
47
+        return $this->has(
48
+            Address::factory()
49
+                ->general()
50
+                ->when($countryCode, function (Factory $factory) use ($countryCode) {
51
+                    return $factory->forCountry($countryCode);
52
+                })
53
+                ->useParentCompany()
54
+        );
48
     }
55
     }
49
 }
56
 }

+ 29
- 1
database/seeders/DatabaseSeeder.php 查看文件

2
 
2
 
3
 namespace Database\Seeders;
3
 namespace Database\Seeders;
4
 
4
 
5
+use App\Models\Company;
5
 use App\Models\User;
6
 use App\Models\User;
6
 use Database\Factories\CompanyFactory;
7
 use Database\Factories\CompanyFactory;
7
 use Illuminate\Database\Seeder;
8
 use Illuminate\Database\Seeder;
14
     public function run(): void
15
     public function run(): void
15
     {
16
     {
16
         // Create a single admin user and their personal company
17
         // Create a single admin user and their personal company
17
-        User::factory()
18
+        $user = User::factory()
18
             ->withPersonalCompany(function (CompanyFactory $factory) {
19
             ->withPersonalCompany(function (CompanyFactory $factory) {
19
                 return $factory
20
                 return $factory
20
                     ->state([
21
                     ->state([
35
                 'password' => bcrypt('password'),
36
                 'password' => bcrypt('password'),
36
                 'current_company_id' => 1,  // Assuming this will be the ID of the created company
37
                 'current_company_id' => 1,  // Assuming this will be the ID of the created company
37
             ]);
38
             ]);
39
+
40
+        $additionalCompanies = [
41
+            ['name' => 'European Retail GmbH', 'country' => 'DE', 'currency' => 'EUR', 'locale' => 'de'],
42
+            ['name' => 'UK Services Ltd', 'country' => 'GB', 'currency' => 'GBP', 'locale' => 'en'],
43
+            ['name' => 'Canadian Manufacturing Inc', 'country' => 'CA', 'currency' => 'CAD', 'locale' => 'en'],
44
+            ['name' => 'Australian Hospitality Pty', 'country' => 'AU', 'currency' => 'AUD', 'locale' => 'en'],
45
+        ];
46
+
47
+        foreach ($additionalCompanies as $companyData) {
48
+            Company::factory()
49
+                ->state([
50
+                    'name' => $companyData['name'],
51
+                    'user_id' => $user->id,
52
+                    'personal_company' => false,
53
+                ])
54
+                ->withCompanyProfile($companyData['country'])
55
+                ->withCompanyDefaults($companyData['currency'], $companyData['locale'])
56
+                ->withTransactions(100)
57
+                ->withOfferings()
58
+                ->withClients()
59
+                ->withVendors()
60
+                ->withInvoices(20)
61
+                ->withRecurringInvoices()
62
+                ->withEstimates(15)
63
+                ->withBills(15)
64
+                ->create();
65
+        }
38
     }
66
     }
39
 }
67
 }

Loading…
取消
儲存