mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-07-31 21:51:38 +00:00
Compare commits
No commits in common. "188444b30f92cdd3990d5c72b53673e28a1f3cbc" and "5e18ae28740122ba765babc7a1953addf18ba59f" have entirely different histories.
188444b30f
...
5e18ae2874
16 changed files with 69 additions and 63 deletions
16
config/packages/dev/easy_log_handler.yaml
Normal file
16
config/packages/dev/easy_log_handler.yaml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
services:
|
||||
EasyCorp\EasyLog\EasyLogHandler:
|
||||
public: false
|
||||
arguments: ['%kernel.logs_dir%/%kernel.environment%.log']
|
||||
|
||||
#// FIXME: How to add this configuration automatically without messing up with the monolog configuration?
|
||||
#monolog:
|
||||
# handlers:
|
||||
# buffered:
|
||||
# type: buffer
|
||||
# handler: easylog
|
||||
# channels: ['!event']
|
||||
# level: debug
|
||||
# easylog:
|
||||
# type: service
|
||||
# id: EasyCorp\EasyLog\EasyLogHandler
|
||||
|
|
@ -20,16 +20,16 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Symfony\Config\DoctrineConfig;
|
||||
|
||||
/**
|
||||
* This file enables native lazy objects on PHP 8.4+.
|
||||
* This class extends the default doctrine ORM configuration to enable native lazy objects on PHP 8.4+.
|
||||
* We have to do this in a PHP file, because the yaml file does not support conditionals on PHP version.
|
||||
*
|
||||
* TODO: Remove this file when we drop support for PHP < 8.4
|
||||
*/
|
||||
|
||||
// On PHP 8.4+ we can use native lazy objects, which are much more efficient than proxies.
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
return ['doctrine' => ['orm' => ['enable_native_lazy_objects' => true]]];
|
||||
}
|
||||
|
||||
return [];
|
||||
return static function(DoctrineConfig $doctrine) {
|
||||
//On PHP 8.4+ we can use native lazy objects, which are much more efficient than proxies.
|
||||
if (PHP_VERSION_ID >= 80400) {
|
||||
$doctrine->orm()->enableNativeLazyObjects(true);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ use App\Doctrine\Functions\SiValueSort;
|
|||
use App\Exceptions\InvalidRegexException;
|
||||
use Doctrine\DBAL\Driver\Connection;
|
||||
use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware;
|
||||
use Pdo\Sqlite;
|
||||
|
||||
/**
|
||||
* This middleware is used to add the regexp operator to the SQLite platform.
|
||||
|
|
@ -45,30 +44,17 @@ class SQLiteRegexExtensionMiddlewareDriver extends AbstractDriverMiddleware
|
|||
if ($params['driver'] === 'pdo_sqlite') {
|
||||
$native_connection = $connection->getNativeConnection();
|
||||
|
||||
//Ensure that the function really exists on the connection, as it is marked as experimental according to PHP documentation
|
||||
if($native_connection instanceof \PDO) {
|
||||
$native_connection->sqliteCreateFunction('REGEXP', self::regexp(...), 2, \PDO::SQLITE_DETERMINISTIC);
|
||||
$native_connection->sqliteCreateFunction('FIELD', self::field(...), -1, \PDO::SQLITE_DETERMINISTIC);
|
||||
$native_connection->sqliteCreateFunction('FIELD2', self::field2(...), 2, \PDO::SQLITE_DETERMINISTIC);
|
||||
|
||||
//Use the new PDO::createFunction and PDO::createCollation methods if available (PHP 8.4+)
|
||||
if (is_a($native_connection, Sqlite::class)) { #TODO: Remove this check when PHP 8.4 is the minimum requirement
|
||||
$native_connection->createFunction('REGEXP', self::regexp(...), 2, Sqlite::DETERMINISTIC);
|
||||
$native_connection->createFunction('FIELD', self::field(...), -1, Sqlite::DETERMINISTIC);
|
||||
$native_connection->createFunction('FIELD2', self::field2(...), 2, Sqlite::DETERMINISTIC);
|
||||
//Create a new collation for natural sorting
|
||||
$native_connection->sqliteCreateCollation('NATURAL_CMP', strnatcmp(...));
|
||||
|
||||
//Create a new collation for natural sorting
|
||||
$native_connection->createCollation('NATURAL_CMP', strnatcmp(...));
|
||||
|
||||
//Create a function for SI prefix value sorting
|
||||
$native_connection->createFunction('SI_VALUE', SiValueSort::sqliteSiValue(...), 1, Sqlite::DETERMINISTIC);
|
||||
} else {
|
||||
$native_connection->sqliteCreateFunction('REGEXP', self::regexp(...), 2, \PDO::SQLITE_DETERMINISTIC);
|
||||
$native_connection->sqliteCreateFunction('FIELD', self::field(...), -1, \PDO::SQLITE_DETERMINISTIC);
|
||||
$native_connection->sqliteCreateFunction('FIELD2', self::field2(...), 2, \PDO::SQLITE_DETERMINISTIC);
|
||||
|
||||
//Create a new collation for natural sorting
|
||||
$native_connection->sqliteCreateCollation('NATURAL_CMP', strnatcmp(...));
|
||||
|
||||
//Create a function for SI prefix value sorting
|
||||
$native_connection->sqliteCreateFunction('SI_VALUE', SiValueSort::sqliteSiValue(...), 1, \PDO::SQLITE_DETERMINISTIC);
|
||||
}
|
||||
//Create a function for SI prefix value sorting
|
||||
$native_connection->sqliteCreateFunction('SI_VALUE', SiValueSort::sqliteSiValue(...), 1, \PDO::SQLITE_DETERMINISTIC);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -132,4 +118,4 @@ class SQLiteRegexExtensionMiddlewareDriver extends AbstractDriverMiddleware
|
|||
|
||||
return $index + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -117,7 +117,7 @@ class Project extends AbstractStructuralDBElement
|
|||
/**
|
||||
* @var string|null The current status of the project
|
||||
*/
|
||||
#[Assert\Choice(choices: ['draft', 'planning', 'in_production', 'finished', 'archived'])]
|
||||
#[Assert\Choice(['draft', 'planning', 'in_production', 'finished', 'archived'])]
|
||||
#[Groups(['extended', 'full', 'project:read', 'project:write', 'import'])]
|
||||
#[ORM\Column(type: Types::STRING, length: 64, nullable: true)]
|
||||
protected ?string $status = null;
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class AttachmentTypeType extends AbstractType
|
|||
return StructuralEntityType::class;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->define('attachment_filter_class')->allowedTypes('null', 'string')->default(null);
|
||||
|
||||
|
|
|
|||
|
|
@ -84,10 +84,8 @@ final class ProjectBuildRequest
|
|||
$remaining_amount = $this->getNeededAmountForBOMEntry($bom_entry);
|
||||
foreach($this->getPartLotsForBOMEntry($bom_entry) as $lot) {
|
||||
//If the lot has instock use it for the build
|
||||
$id = $lot->getID() ?? throw new \RuntimeException("Part lot needs to have an ID!");
|
||||
|
||||
$this->withdraw_amounts[$id] = min($remaining_amount, $lot->getAmount());
|
||||
$remaining_amount -= max(0, $this->withdraw_amounts[$id]);
|
||||
$this->withdraw_amounts[$lot->getID()] = min($remaining_amount, $lot->getAmount());
|
||||
$remaining_amount -= max(0, $this->withdraw_amounts[$lot->getID()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -178,10 +176,6 @@ final class ProjectBuildRequest
|
|||
{
|
||||
$lot_id = $lot instanceof PartLot ? $lot->getID() : $lot;
|
||||
|
||||
if ($lot_id === null) {
|
||||
throw new \InvalidArgumentException('The given lot must have an ID!');
|
||||
}
|
||||
|
||||
if (! array_key_exists($lot_id, $this->withdraw_amounts)) {
|
||||
throw new \InvalidArgumentException('The given lot is not in the withdraw amounts array!');
|
||||
}
|
||||
|
|
@ -198,12 +192,10 @@ final class ProjectBuildRequest
|
|||
{
|
||||
if ($lot instanceof PartLot) {
|
||||
$lot_id = $lot->getID();
|
||||
} else {
|
||||
} elseif (is_int($lot)) {
|
||||
$lot_id = $lot;
|
||||
}
|
||||
|
||||
if ($lot_id === null) {
|
||||
throw new \InvalidArgumentException('The given lot must have an ID!');
|
||||
} else {
|
||||
throw new \InvalidArgumentException('The given lot must be an instance of PartLot or an ID of a PartLot!');
|
||||
}
|
||||
|
||||
$this->withdraw_amounts[$lot_id] = $amount;
|
||||
|
|
@ -304,7 +296,7 @@ final class ProjectBuildRequest
|
|||
* @param bool $dont_check_quantity
|
||||
* @return $this
|
||||
*/
|
||||
public function setDontCheckQuantity(bool $dont_check_quantity): self
|
||||
public function setDontCheckQuantity(bool $dont_check_quantity): ProjectBuildRequest
|
||||
{
|
||||
$this->dont_check_quantity = $dont_check_quantity;
|
||||
return $this;
|
||||
|
|
|
|||
|
|
@ -158,6 +158,7 @@ class DBElementRepository extends EntityRepository
|
|||
{
|
||||
$reflection = new ReflectionClass($element::class);
|
||||
$property = $reflection->getProperty($field);
|
||||
$property->setAccessible(true);
|
||||
$property->setValue($element, $new_value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,6 +233,7 @@ trait PKImportHelperTrait
|
|||
|
||||
$reflectionClass = new \ReflectionClass($entity);
|
||||
$property = $reflectionClass->getProperty('addedDate');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue($entity, $date);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
<p class="m-0">
|
||||
<b>{% trans %}log.collection_deleted.deleted{% endtrans %}</b>:
|
||||
{{ type_label(entry.deletedElementClass) }} #{{ entry.deletedElementID }}
|
||||
{{ entity_type_label(entry.deletedElementClass) }} #{{ entry.deletedElementID }}
|
||||
{% if entry.oldName is not empty %}
|
||||
({{ entry.oldName }})
|
||||
{% endif %}
|
||||
|
|
@ -12,4 +12,4 @@
|
|||
<p class="m-0">
|
||||
<b>{% trans %}log.collection_deleted.on_collection{% endtrans %}</b>:
|
||||
{{ log_helper.translate_field(entry.collectionName) }}
|
||||
</p>
|
||||
</p>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<div class="carousel-caption text-white">
|
||||
<div><b>{{ pic.name }}</b></div>
|
||||
<div>{% if pic.filename %}({{ pic.filename }}) {% endif %}</div>
|
||||
<div>{{ type_label(pic.element) }}</div>
|
||||
<div>{{ entity_type_label(pic.element) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
@ -41,4 +41,4 @@
|
|||
|
||||
{% else %}
|
||||
<img src="{{ asset('img/part_placeholder.svg') }}" class="img-fluid img-thumbnail bg-light mb-2" alt="Part main image" height="300" width="300">
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
<i>({{ timeTravel | format_datetime('short') }})</i>
|
||||
{% endif %}
|
||||
{% if part.projectBuildPart %}
|
||||
(<i>{{ type_label(part.builtProject) }}</i>: <a class="text-white" href="{{ entity_url(part.builtProject) }}">{{ part.builtProject.name }}</a>)
|
||||
(<i>{{ entity_type_label(part.builtProject) }}</i>: <a class="text-white" href="{{ entity_url(part.builtProject) }}">{{ part.builtProject.name }}</a>)
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="float-end">
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ use App\Doctrine\Functions\SiValueSort;
|
|||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
|
||||
use Doctrine\DBAL\Platforms\SQLitePlatform;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
|
||||
final class SiValueSortTest extends AbstractDoctrineFunctionTestCase
|
||||
{
|
||||
|
|
@ -72,7 +71,9 @@ final class SiValueSortTest extends AbstractDoctrineFunctionTestCase
|
|||
$this->assertSame('SI_VALUE(part_name)', $sql);
|
||||
}
|
||||
|
||||
#[DataProvider('sqliteSiValueProvider')]
|
||||
/**
|
||||
* @dataProvider sqliteSiValueProvider
|
||||
*/
|
||||
public function testSqliteSiValue(?string $input, ?float $expected): void
|
||||
{
|
||||
$result = SiValueSort::sqliteSiValue($input);
|
||||
|
|
|
|||
|
|
@ -276,6 +276,7 @@ final class AttachmentTest extends TestCase
|
|||
{
|
||||
$reflection = new ReflectionClass($object);
|
||||
$reflection_property = $reflection->getProperty($property);
|
||||
$reflection_property->setAccessible(true);
|
||||
$reflection_property->setValue($object, $value);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,12 +82,7 @@ final class ProjectBuildRequestTest extends TestCase
|
|||
|
||||
$part2->setName('Part 2');
|
||||
$part2->setPartUnit($float_unit);
|
||||
$this->lot2 = new class extends PartLot {
|
||||
public function getID(): ?int
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
};;
|
||||
$this->lot2 = new PartLot();
|
||||
$part2->addPartLot($this->lot2);
|
||||
$this->lot2->setAmount(2.5);
|
||||
$this->lot2->setDescription('Lot 2');
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ final class BuerklinProviderTest extends TestCase
|
|||
public function testAttributesToParametersParsesUnitsAndValues(): void
|
||||
{
|
||||
$method = new \ReflectionMethod(BuerklinProvider::class, 'attributesToParameters');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$features = [
|
||||
[
|
||||
|
|
@ -126,6 +127,7 @@ final class BuerklinProviderTest extends TestCase
|
|||
public function testComplianceParameters(): void
|
||||
{
|
||||
$method = new \ReflectionMethod(BuerklinProvider::class, 'complianceToParameters');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$product = [
|
||||
'labelRoHS' => 'Yes',
|
||||
|
|
@ -156,6 +158,7 @@ final class BuerklinProviderTest extends TestCase
|
|||
public function testImageSelectionPrefersZoomAndDeduplicates(): void
|
||||
{
|
||||
$method = new \ReflectionMethod(BuerklinProvider::class, 'getProductImages');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$images = [
|
||||
['format' => 'product', 'url' => '/img/a.webp'],
|
||||
|
|
@ -173,6 +176,7 @@ final class BuerklinProviderTest extends TestCase
|
|||
public function testFootprintExtraction(): void
|
||||
{
|
||||
$method = new \ReflectionMethod(BuerklinProvider::class, 'getPartDetail');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$product = [
|
||||
'code' => 'TEST1',
|
||||
|
|
@ -208,6 +212,7 @@ final class BuerklinProviderTest extends TestCase
|
|||
];
|
||||
|
||||
$method = new \ReflectionMethod(BuerklinProvider::class, 'pricesToVendorInfo');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$vendorInfo = $method->invoke($this->provider, 'SKU1', 'https://x', $detailPrice);
|
||||
|
||||
|
|
@ -255,6 +260,7 @@ final class BuerklinProviderTest extends TestCase
|
|||
);
|
||||
|
||||
$method = new \ReflectionMethod(BuerklinProvider::class, 'convertPartDetailToSearchResult');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$dto = $method->invoke($this->provider, $detail);
|
||||
|
||||
|
|
@ -267,13 +273,13 @@ final class BuerklinProviderTest extends TestCase
|
|||
{
|
||||
$this->assertSame(['buerklin.com'], $this->provider->getHandledDomains());
|
||||
}
|
||||
|
||||
|
||||
#[DataProvider('buerklinIdFromUrlProvider')]
|
||||
public function testGetIDFromURLExtractsId(string $url, ?string $expected): void
|
||||
{
|
||||
$this->assertSame($expected, $this->provider->getIDFromURL($url));
|
||||
}
|
||||
|
||||
|
||||
public static function buerklinIdFromUrlProvider(): \Iterator
|
||||
{
|
||||
yield 'de long path' => [
|
||||
|
|
|
|||
|
|
@ -367,6 +367,7 @@ final class LCSCProviderTest extends TestCase
|
|||
{
|
||||
$reflection = new \ReflectionClass($this->provider);
|
||||
$method = $reflection->getMethod('sanitizeField');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$this->assertNull($method->invokeArgs($this->provider, [null]));
|
||||
$this->assertEquals('Clean text', $method->invokeArgs($this->provider, ['Clean text']));
|
||||
|
|
@ -377,6 +378,7 @@ final class LCSCProviderTest extends TestCase
|
|||
{
|
||||
$reflection = new \ReflectionClass($this->provider);
|
||||
$method = $reflection->getMethod('getUsedCurrency');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$this->assertEquals('USD', $method->invokeArgs($this->provider, ['US$']));
|
||||
$this->assertEquals('USD', $method->invokeArgs($this->provider, ['$']));
|
||||
|
|
@ -389,6 +391,7 @@ final class LCSCProviderTest extends TestCase
|
|||
{
|
||||
$reflection = new \ReflectionClass($this->provider);
|
||||
$method = $reflection->getMethod('getProductShortURL');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$result = $method->invokeArgs($this->provider, ['C123456']);
|
||||
$this->assertEquals('https://www.lcsc.com/product-detail/C123456.html', $result);
|
||||
|
|
@ -398,6 +401,7 @@ final class LCSCProviderTest extends TestCase
|
|||
{
|
||||
$reflection = new \ReflectionClass($this->provider);
|
||||
$method = $reflection->getMethod('getProductDatasheets');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$result = $method->invokeArgs($this->provider, [null]);
|
||||
$this->assertIsArray($result);
|
||||
|
|
@ -413,6 +417,7 @@ final class LCSCProviderTest extends TestCase
|
|||
{
|
||||
$reflection = new \ReflectionClass($this->provider);
|
||||
$method = $reflection->getMethod('getProductImages');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$result = $method->invokeArgs($this->provider, [null]);
|
||||
$this->assertIsArray($result);
|
||||
|
|
@ -429,6 +434,7 @@ final class LCSCProviderTest extends TestCase
|
|||
{
|
||||
$reflection = new \ReflectionClass($this->provider);
|
||||
$method = $reflection->getMethod('attributesToParameters');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$attributes = [
|
||||
['paramNameEn' => 'Resistance', 'paramValueEn' => '1kΩ'],
|
||||
|
|
@ -448,6 +454,7 @@ final class LCSCProviderTest extends TestCase
|
|||
{
|
||||
$reflection = new \ReflectionClass($this->provider);
|
||||
$method = $reflection->getMethod('pricesToVendorInfo');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$prices = [
|
||||
['ladder' => 1, 'productPrice' => '0.10', 'currencySymbol' => 'US$'],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue