Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Kanvas\Regions\Models\Regions;
use Kanvas\SystemModules\Models\SystemModules;
use Kanvas\SystemModules\Services\SystemModulesServices;
use Kanvas\Users\Repositories\UsersRepository;
use League\Csv\Reader;

class FilesystemMapperMutation
Expand Down Expand Up @@ -117,6 +118,12 @@ public function process(mixed $root, array $req): FilesystemImports
? Regions::getByIdFromCompanyAppOrGlobal($input['regions_id'], $company, $app)
: Regions::getDefault($company, $app);

$extra = is_array($input['extra'] ?? null) ? $input['extra'] : [];
if (! empty($input['users_id'])) {
$owner = UsersRepository::getUserOfCompanyById($company, (int) $input['users_id']);
$extra['users_id'] = $owner->getId();
}

$dto = FilesystemImport::from([
'app' => $app,
'users' => $user,
Expand All @@ -125,7 +132,7 @@ public function process(mixed $root, array $req): FilesystemImports
'companiesBranches' => $user->getCurrentBranch(),
'filesystem' => $filesystem,
'filesystemMapper' => $mapper,
'extra' => $input['extra'] ?? null,
'extra' => $extra,
]);

if ($app->get(AppSettingsEnums::FILESYSTEM_MAPPER_HEADER_VALIDATION->getValue())) {
Expand Down
1 change: 1 addition & 0 deletions graphql/schemas/Ecosystem/Filesystem/mapper.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ input FilesystemImportInput {
regions_id: ID
filesystem_mapper_id: ID!
filesystem_id: ID!
users_id: ID
extra: Mixed
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ public function __construct(
public UserInterface $user,
public KanvasRegions|Regions $region,
public ?AppInterface $app = null,
public bool $runWorkflow = true
public bool $runWorkflow = true,
public ?UserInterface $ownerUser = null
) {
$this->app = $this->app ?? app(Apps::class);
}
Expand All @@ -59,6 +60,7 @@ public function execute(): ProductsModel
'app' => $this->app,
'company' => $this->company,
'user' => $this->user,
'ownerUser' => $this->ownerUser,
'name' => $this->importedProduct->name,
'slug' => $this->importedProduct->slug,
'description' => $this->importedProduct->description,
Expand Down
12 changes: 11 additions & 1 deletion src/Domains/Inventory/Importer/Jobs/ProductImporterJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Kanvas\Inventory\Importer\Actions\ProductImporterAction;
use Kanvas\Inventory\Importer\DataTransferObjects\ProductImporter;
use Kanvas\Inventory\Importer\Events\ProductImportEvent;
use Kanvas\Users\Repositories\UsersRepository;
use Kanvas\Workflow\Enums\WorkflowEnum;
use Nuwave\Lighthouse\Execution\Utils\Subscription;
use Override;
Expand Down Expand Up @@ -44,6 +45,14 @@ public function handle()
$this->startFilesystemMapperImport();
$processProducts = [];

// Optional owner: stamps products/variants users_id; $this->user still gates auth + publishing.
$ownerUser = null;
$extra = $this->filesystemImport?->extra;
$ownerId = is_array($extra) ? ($extra['users_id'] ?? null) : null;
if (! empty($ownerId)) {
$ownerUser = UsersRepository::getUserOfCompanyById($company, (int) $ownerId);
}

foreach ($this->iterateImporterRows() as $request) {
try {
$product = new ProductImporterAction(
Expand All @@ -52,7 +61,8 @@ public function handle()
$this->user,
$this->region,
$this->app,
$this->runWorkflow
$this->runWorkflow,
$ownerUser
)->execute();
if ($product->wasRecentlyCreated) {
$created++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function execute(): Products
'warranty_terms' => $this->productDto->warranty_terms,
'upc' => $this->productDto->upc,
'status_id' => $this->productDto->status_id,
'users_id' => $this->user->getId(),
'users_id' => ($this->productDto->ownerUser ?? $this->user)->getId(),
'weight' => $this->productDto->weight ?? $existingProduct?->weight ?? 0,
];

Expand Down Expand Up @@ -97,7 +97,7 @@ public function execute(): Products
}

if ($this->productDto->variants) {
VariantService::createVariantsFromArray($products, $this->productDto->variants, $this->user);
VariantService::createVariantsFromArray($products, $this->productDto->variants, $this->user, $this->productDto->ownerUser);
} else {
VariantService::createDefaultVariant($products, $this->user, $this->productDto);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public function __construct(
public array $files = [],
public ?string $slug = null,
public ?float $weight = null,
public ?UserInterface $ownerUser = null,
) {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public function execute(): Variants
$search,
[
'name' => $this->variantDto->name,
'users_id' => $this->user->getId(),
'users_id' => ($this->variantDto->ownerUser ?? $this->user)->getId(),
'slug' => $this->variantDto->slug ?? Str::slug($this->variantDto->name),
'description' => $this->variantDto->getDescription(),
'short_description' => $this->variantDto->short_description,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public function execute(): Variants
'name' => $this->variantDto->name,
'slug' => $this->variantDto->slug ?? Str::slug($this->variantDto->name),
'sku' => $this->variantDto->sku,
'users_id' => $this->user->getId(),
'users_id' => ($this->variantDto->ownerUser ?? $this->user)->getId(),
'description' => $this->variantDto->getDescription() ?? $this->variant->description,
'short_description' => $this->variantDto->short_description ?? $this->variant->short_description,
'html_description' => ! empty($this->variantDto->html_description)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public function __construct(
public ?string $slug = null,
public array $files = [],
public ?float $weight = null,
public ?bool $is_published = true
public ?bool $is_published = true,
public ?UserInterface $ownerUser = null
) {
}

Expand Down
10 changes: 8 additions & 2 deletions src/Domains/Inventory/Variants/Services/VariantService.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,12 @@ class VariantService
/**
* Create a new product variants.
*/
public static function createVariantsFromArray(Products $product, array $variants, UserInterface $user): array
{
public static function createVariantsFromArray(
Products $product,
array $variants,
UserInterface $user,
?UserInterface $ownerUser = null
): array {
$variantsData = [];

foreach ($variants as $variant) {
Expand All @@ -54,6 +58,7 @@ public static function createVariantsFromArray(Products $product, array $variant
'product' => $product,
'products_id' => $product->getId(),
...$variant,
'ownerUser' => $ownerUser,
]);

$existVariantUpdate = Variants::fromCompany($product->company)->fromApp($product->app)->where('sku', $variantDto->sku);
Expand Down Expand Up @@ -166,6 +171,7 @@ public static function createDefaultVariant(Products $product, UserInterface $us
'product' => $product,
'products_id' => $product->getId(),
...$variant,
'ownerUser' => $productDto?->ownerUser,
]);
$variantModel = (new CreateVariantsAction($variantDto, $user))->execute();

Expand Down
180 changes: 180 additions & 0 deletions tests/Inventory/Integration/Imports/ProductImporterOwnerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
<?php

declare(strict_types=1);

namespace Tests\Inventory\Integration\Imports;

use Kanvas\Apps\Models\Apps;
use Kanvas\Currencies\Models\Currencies;
use Kanvas\Inventory\Importer\Actions\ProductImporterAction;
use Kanvas\Inventory\Importer\DataTransferObjects\ProductImporter;
use Kanvas\Inventory\Products\Models\Products;
use Kanvas\Inventory\ProductsTypes\Actions\CreateProductTypeAction;
use Kanvas\Inventory\ProductsTypes\DataTransferObject\ProductsTypes as ProductsTypesDto;
use Kanvas\Inventory\Regions\Actions\CreateRegionAction;
use Kanvas\Inventory\Regions\DataTransferObject\Region;
use Kanvas\Inventory\Warehouses\Actions\CreateWarehouseAction;
use Kanvas\Inventory\Warehouses\DataTransferObject\Warehouses;
use Kanvas\Users\Models\Users;
use Tests\TestCase;

/**
* Optional import owner: $ownerUser stamps products/variants users_id while the
* acting user still gates publishing; no owner keeps the old behavior.
*/
final class ProductImporterOwnerTest extends TestCase
{
public function testImportStampsOwnerOnProductAndVariantsWhenOwnerProvided(): void
{
$user = auth()->user();
$app = app(Apps::class);
$company = $user->getCurrentCompany();
$region = $this->makeRegion($user, $company, $app);
$this->makeWarehouse($user, $company, $app, $region);
$productType = $this->makeProductType($user, $company);

$owner = Users::factory()->create();
$slug = 'owner-prod-' . uniqid();
$sku = 'OWNER-SKU-' . uniqid();

new ProductImporterAction(
ProductImporter::from([
'name' => 'Owner Product',
'slug' => $slug,
'sku' => $sku,
'description' => 'Owned by a different user',
'variants' => [
['name' => 'Owner Variant', 'sku' => $sku, 'price' => 10.0, 'quantity' => 5],
],
'productType' => ['name' => $productType->name, 'weight' => 1],
]),
$company,
$user,
$region,
$app,
false,
$owner,
)->execute();

$product = Products::where('slug', $slug)
->where('apps_id', $app->getId())
->where('companies_id', $company->getId())
->first();

$this->assertNotNull($product);
$this->assertSame(
$owner->getId(),
(int) $product->users_id,
'Product users_id must be the selected owner, not the importer',
);
$this->assertTrue(
(bool) $product->is_published,
'Importer is admin, so the product still publishes even though the owner is a regular user',
);

$variants = $product->variants()->get();
$this->assertGreaterThan(0, $variants->count());
foreach ($variants as $variant) {
$this->assertSame(
$owner->getId(),
(int) $variant->users_id,
'Each variant users_id must be the selected owner',
);
}
}

public function testImportUsesActingUserWhenNoOwnerProvided(): void
{
$user = auth()->user();
$app = app(Apps::class);
$company = $user->getCurrentCompany();
$region = $this->makeRegion($user, $company, $app);
$this->makeWarehouse($user, $company, $app, $region);
$productType = $this->makeProductType($user, $company);

$slug = 'default-owner-prod-' . uniqid();
$sku = 'DEFAULT-SKU-' . uniqid();

new ProductImporterAction(
ProductImporter::from([
'name' => 'Default Owner Product',
'slug' => $slug,
'sku' => $sku,
'variants' => [
['name' => 'Default Variant', 'sku' => $sku, 'price' => 10.0, 'quantity' => 5],
],
'productType' => ['name' => $productType->name, 'weight' => 1],
]),
$company,
$user,
$region,
$app,
false,
)->execute();

$product = Products::where('slug', $slug)
->where('apps_id', $app->getId())
->where('companies_id', $company->getId())
->first();

$this->assertNotNull($product);
$this->assertSame(
$user->getId(),
(int) $product->users_id,
'Without an owner the importer owns the product, exactly as before',
);

foreach ($product->variants()->get() as $variant) {
$this->assertSame($user->getId(), (int) $variant->users_id);
}
}

private function makeRegion($user, $company, $app): object
{
return new CreateRegionAction(
new Region(
$company,
$app,
$user,
Currencies::getById(1),
'Region ' . uniqid(),
'r-' . uniqid(),
null,
1,
),
$user,
)->execute();
}

private function makeWarehouse($user, $company, $app, $region): void
{
new CreateWarehouseAction(
new Warehouses(
$company,
$app,
$user,
$region,
'Warehouse ' . uniqid(),
'Test Location',
true,
true,
),
$user,
)->execute();
}

private function makeProductType($user, $company): object
{
return new CreateProductTypeAction(
new ProductsTypesDto(
$company,
$user,
'Owner Test Type ' . uniqid(),
'Type used by the owner import test',
1,
true,
),
$user,
)->execute();
}
}
Loading