mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-03-01 13:05:15 +01:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca16763423 | ||
|
|
b6dd5bb881 | ||
|
|
f8e299ec56 | ||
|
|
91e9c6e048 | ||
|
|
b941b97eee | ||
|
|
d38ac652fc | ||
|
|
bdcf3b71ce | ||
|
|
ddbf8b7725 | ||
|
|
a6fd4547a7 | ||
|
|
15e072a2ff | ||
|
|
f98e20aa84 | ||
|
|
e7a1b33ae6 | ||
|
|
2d5f23271f | ||
|
|
059110ae7a |
@@ -34,7 +34,7 @@
|
||||
PassEnv ERROR_PAGE_ADMIN_EMAIL ERROR_PAGE_SHOW_HELP
|
||||
PassEnv DEMO_MODE NO_URL_REWRITE_AVAILABLE FIXER_API_KEY BANNER
|
||||
PassEnv SAML_ENABLED SAML_ROLE_MAPPING SAML_UPDATE_GROUP_ON_LOGIN SAML_IDP_ENTITY_ID SAML_IDP_SINGLE_SIGN_ON_SERVICE SAML_IDP_SINGLE_LOGOUT_SERVICE SAML_IDP_X509_CERT SAML_SP_ENTITY_ID SAML_SP_X509_CERT SAMLP_SP_PRIVATE_KEY
|
||||
|
||||
PassEnv TABLE_DEFAULT_PAGE_SIZE
|
||||
|
||||
# For most configuration files from conf-available/, which are
|
||||
# enabled or disabled at a global level, it is possible to
|
||||
|
||||
7
.env
7
.env
@@ -84,6 +84,13 @@ ERROR_PAGE_ADMIN_EMAIL=''
|
||||
# If this is set to true, solutions to common problems are shown on error pages. Disable this, if you do not want your users to see them...
|
||||
ERROR_PAGE_SHOW_HELP=1
|
||||
|
||||
##################################################################################
|
||||
# Part table settings
|
||||
##################################################################################
|
||||
|
||||
# The default page size for the part table (set to -1 to show all parts on one page)
|
||||
TABLE_DEFAULT_PAGE_SIZE=50
|
||||
|
||||
###################################################################################
|
||||
# SAML Single sign on-settings
|
||||
###################################################################################
|
||||
|
||||
@@ -70,9 +70,9 @@ export default class extends Controller {
|
||||
|
||||
if (data) {
|
||||
//Do not save the start value (current page), as we want to always start at the first page on a page reload
|
||||
data.start = 0;
|
||||
//50 is the default length supplied by datatables, reset it to that value
|
||||
data.length = 50;
|
||||
delete data.start;
|
||||
//Reset the data length to the default value by deleting the length property
|
||||
delete data.length;
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
@@ -72,6 +72,17 @@
|
||||
}
|
||||
} else {
|
||||
request._dt = config.name;
|
||||
|
||||
//Try to resolve the original column index when the column was reordered (using the ColReorder plugin)
|
||||
//Only do this when _ColReorder_iOrigCol is available
|
||||
if (settings.aoColumns && settings.aoColumns.length && settings.aoColumns[0]._ColReorder_iOrigCol !== undefined) {
|
||||
if (request.order && request.order.length) {
|
||||
request.order.forEach(function (order) {
|
||||
order.column = settings.aoColumns[order.column]._ColReorder_iOrigCol;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$.ajax(typeof config.url === 'function' ? config.url(dt) : config.url, {
|
||||
method: config.method,
|
||||
data: request
|
||||
|
||||
@@ -59,13 +59,16 @@ class RegisterEventHelper {
|
||||
}
|
||||
|
||||
registerTooltips() {
|
||||
this.registerLoadHandler(() => {
|
||||
const handler = () => {
|
||||
$(".tooltip").remove();
|
||||
//Exclude dropdown buttons from tooltips, otherwise we run into endless errors from bootstrap (bootstrap.esm.js:614 Bootstrap doesn't allow more than one instance per element. Bound instance: bs.dropdown.)
|
||||
$('a[title], label[title], button[title]:not([data-bs-toggle="dropdown"]), p[title], span[title], h6[title], h3[title], i.fas[title]')
|
||||
$('a[title], label[title], button[title]:not([data-bs-toggle="dropdown"]), p[title], span[title], h6[title], h3[title], i[title]')
|
||||
//@ts-ignore
|
||||
.tooltip("hide").tooltip({container: "body", placement: "auto", boundary: 'window'});
|
||||
});
|
||||
};
|
||||
|
||||
this.registerLoadHandler(handler);
|
||||
document.addEventListener('dt:loaded', handler);
|
||||
}
|
||||
|
||||
registerSpecialCharInput() {
|
||||
|
||||
@@ -9,7 +9,7 @@ datatables:
|
||||
# Set options, as documented at https://datatables.net/reference/option/
|
||||
options:
|
||||
lengthMenu : [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]]
|
||||
pageLength: 50
|
||||
pageLength: '%partdb.table.default_page_size%' # Set to -1 to disable pagination (i.e. show all rows) by default
|
||||
#dom: "<'row' <'col-sm-12' tr>><'row' <'col-sm-6'l><'col-sm-6 text-right'pif>>"
|
||||
dom: " <'row'<'col mb-2 input-group' B l> <'col mb-2' <'pull-end' p>>>
|
||||
<'card'
|
||||
|
||||
@@ -48,6 +48,11 @@ parameters:
|
||||
######################################################################################################################
|
||||
partdb.saml.enabled: '%env(bool:SAML_ENABLED)%' # If this is set to true, SAML authentication is enabled
|
||||
|
||||
######################################################################################################################
|
||||
# Table settings
|
||||
######################################################################################################################
|
||||
partdb.table.default_page_size: '%env(int:TABLE_DEFAULT_PAGE_SIZE)%' # The default number of entries shown per page in tables
|
||||
|
||||
######################################################################################################################
|
||||
# Sidebar
|
||||
######################################################################################################################
|
||||
@@ -119,6 +124,8 @@ parameters:
|
||||
env(EMAIL_SENDER_NAME): 'Part-DB Mailer'
|
||||
env(ALLOW_EMAIL_PW_RESET): 0
|
||||
|
||||
env(TABLE_DEFAULT_PAGE_SIZE): 50
|
||||
|
||||
env(TRUSTED_PROXIES): '127.0.0.1' #By default trust only our own server
|
||||
env(TRUSTED_HOSTS): '' # Trust all host names by default
|
||||
|
||||
|
||||
@@ -43,6 +43,9 @@ The following configuration options can only be changed by the server administra
|
||||
* `EMAIL_SENDER_NAME`: Similar to `EMAIL_SENDER_EMAIL` but this allows you to specify the name from which the mails are sent from.
|
||||
* `ALLOW_EMAIL_PW_RESET`: Set this value to true, if you wan to allow users to reset their password via an email notification. You have to configure the mailprovider first before via the MAILER_DSN setting.
|
||||
|
||||
### Table related settings
|
||||
* `TABLE_DEFAULT_PAGE_SIZE`: The default page size for tables. This is the number of rows which are shown per page. Set to `-1` to disable pagination and show all rows at once.
|
||||
|
||||
### History/Eventlog related settings
|
||||
The following options are used to configure, which (and how much) data is written to the system log:
|
||||
* `HISTORY_SAVE_CHANGED_FIELDS`: When this option is set to true, the name of the fields which are changed, are saved to the DB (so for example it is logged that a user has changed, that the user has changed the name and description of the field, but not the data/content of these changes)
|
||||
|
||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
namespace App\DataTables\Column;
|
||||
|
||||
use App\Entity\Base\AbstractNamedDBElement;
|
||||
use App\Entity\Base\AbstractStructuralDBElement;
|
||||
use App\Services\EntityURLGenerator;
|
||||
use Omines\DataTablesBundle\Column\AbstractColumn;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
@@ -70,8 +71,9 @@ class EntityColumn extends AbstractColumn
|
||||
if ($entity instanceof AbstractNamedDBElement) {
|
||||
if (null !== $entity->getID()) {
|
||||
return sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
'<a href="%s" title="%s">%s</a>',
|
||||
$this->urlGenerator->listPartsURL($entity),
|
||||
$entity instanceof AbstractStructuralDBElement ? htmlspecialchars($entity->getFullPath()) : htmlspecialchars($entity->getName()),
|
||||
htmlspecialchars($entity->getName())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -144,8 +144,9 @@ final class PartsDataTable implements DataTableTypeInterface
|
||||
continue;
|
||||
}
|
||||
$tmp[] = sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
'<a href="%s" title="%s">%s</a>',
|
||||
$this->urlGenerator->listPartsURL($lot->getStorageLocation()),
|
||||
htmlspecialchars($lot->getStorageLocation()->getFullPath()),
|
||||
htmlspecialchars($lot->getStorageLocation()->getName())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ class Currency extends AbstractStructuralDBElement
|
||||
* @var string the 3-letter ISO code of the currency
|
||||
*/
|
||||
#[Assert\Currency]
|
||||
#[Assert\NotBlank]
|
||||
#[Groups(['extended', 'full', 'import'])]
|
||||
#[ORM\Column(type: Types::STRING)]
|
||||
protected string $iso_code = "";
|
||||
@@ -113,12 +114,12 @@ class Currency extends AbstractStructuralDBElement
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIsoCode(): ?string
|
||||
public function getIsoCode(): string
|
||||
{
|
||||
return $this->iso_code;
|
||||
}
|
||||
|
||||
public function setIsoCode(?string $iso_code): self
|
||||
public function setIsoCode(string $iso_code): self
|
||||
{
|
||||
$this->iso_code = $iso_code;
|
||||
|
||||
@@ -156,12 +157,15 @@ class Currency extends AbstractStructuralDBElement
|
||||
*/
|
||||
public function setExchangeRate(?BigDecimal $exchange_rate): self
|
||||
{
|
||||
if (!$exchange_rate instanceof BigDecimal) {
|
||||
//If the exchange rate is null, set it to null and return.
|
||||
if ($exchange_rate === null) {
|
||||
$this->exchange_rate = null;
|
||||
return $this;
|
||||
}
|
||||
$tmp = $exchange_rate->toScale(self::PRICE_SCALE, RoundingMode::HALF_UP);
|
||||
|
||||
//Only change the object, if the value changes, so that doctrine does not detect it as changed.
|
||||
if ((string) $tmp !== (string) $this->exchange_rate) {
|
||||
//Or if the current exchange rate is currently null, as we can not compare it then
|
||||
if ($this->exchange_rate === null || $exchange_rate->compareTo($this->exchange_rate) !== 0) {
|
||||
$this->exchange_rate = $exchange_rate;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class CurrencyAdminForm extends BaseEntityAdminForm
|
||||
$is_new = null === $entity->getID();
|
||||
|
||||
$builder->add('iso_code', CurrencyType::class, [
|
||||
'required' => false,
|
||||
'required' => true,
|
||||
'label' => 'currency.edit.iso_code',
|
||||
'preferred_choices' => ['EUR', 'USD', 'GBP', 'JPY', 'CNY'],
|
||||
'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity),
|
||||
|
||||
@@ -50,10 +50,12 @@ class ParameterConstraintType extends AbstractType
|
||||
|
||||
$builder->add('unit', SearchType::class, [
|
||||
'required' => false,
|
||||
'empty_data' => '',
|
||||
]);
|
||||
|
||||
$builder->add('symbol', SearchType::class, [
|
||||
'required' => false
|
||||
'required' => false,
|
||||
'empty_data' => '',
|
||||
]);
|
||||
|
||||
$builder->add('value_text', TextConstraintType::class, [
|
||||
|
||||
@@ -96,7 +96,7 @@ class StructuralEntityChoiceHelper
|
||||
public function generateChoiceAttrCurrency(Currency $choice, Options|array $options): array
|
||||
{
|
||||
$tmp = $this->generateChoiceAttr($choice, $options);
|
||||
$symbol = $choice->getIsoCode() === null || $choice->getIsoCode() === '' ? null : Currencies::getSymbol($choice->getIsoCode());
|
||||
$symbol = empty($choice->getIsoCode()) ? null : Currencies::getSymbol($choice->getIsoCode());
|
||||
$tmp['data-short'] = $options['short'] ? $symbol : $choice->getName();
|
||||
|
||||
return $tmp + [
|
||||
|
||||
@@ -75,7 +75,7 @@ class PKPartImporter
|
||||
$entity->setComment('This part represents a former metapart in PartKeepr. It is not supported in Part-DB yet. And you can most likely delete it.');
|
||||
$entity->setTags('partkeepr-imported,partkeepr-metapart');
|
||||
} else {
|
||||
$entity->setMinAmount($part['minStockLevel'] ?? 0);
|
||||
$entity->setMinAmount((float) ($part['minStockLevel'] ?? 0));
|
||||
if (!empty($part['internalPartNumber'])) {
|
||||
$entity->setIpn($part['internalPartNumber']);
|
||||
}
|
||||
@@ -92,7 +92,7 @@ class PKPartImporter
|
||||
|
||||
//Create a part lot to store the stock level and location
|
||||
$lot = new PartLot();
|
||||
$lot->setAmount($part['stockLevel'] ?? 0);
|
||||
$lot->setAmount((float) ($part['stockLevel'] ?? 0));
|
||||
$this->setAssociationField($lot, 'storage_location', Storelocation::class, $part['storageLocation_id']);
|
||||
$entity->addPartLot($lot);
|
||||
|
||||
@@ -278,7 +278,7 @@ class PKPartImporter
|
||||
$orderdetail->addPricedetail($pricedetail);
|
||||
//Partkeepr stores the price per item, we need to convert it to the price per packaging unit
|
||||
$price_per_item = BigDecimal::of($partdistributor['price']);
|
||||
$packaging_unit = $partdistributor['packagingUnit'] ?? 1;
|
||||
$packaging_unit = (float) ($partdistributor['packagingUnit'] ?? 1);
|
||||
$pricedetail->setPrice($price_per_item->multipliedBy($packaging_unit));
|
||||
$pricedetail->setPriceRelatedQuantity($packaging_unit);
|
||||
//We have to set the minimum discount quantity to the packaging unit (PartKeepr does not know this concept)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{% set flash_bg = label|replace({'success': 'bg-success text-white',
|
||||
'error': 'bg-danger text-white', 'warning': 'bg-warning text-white',
|
||||
'notice': 'bg-info text-white', 'info': 'bg-light'})%}
|
||||
'notice': 'bg-info text-white', 'info': 'bg-body-tertiary'})%}
|
||||
|
||||
<div class="toast shadow" role="alert" aria-live="assertive" aria-atomic="true" data-delay="5000" {{ stimulus_controller('common/toast') }}>
|
||||
<div class="toast-header {{ flash_bg }}">
|
||||
|
||||
@@ -37,6 +37,7 @@ hr {
|
||||
|
||||
.qr {
|
||||
max-width: 80%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.qr-container a {
|
||||
|
||||
@@ -70,14 +70,15 @@
|
||||
{% set min_order_amount = pricedetail_helper.minOrderAmount(part) %}
|
||||
{% set max_order_amount = pricedetail_helper.maxDiscountAmount(part) %}
|
||||
{% set max_order_price = pricedetail_helper.calculateAvgPrice(part, max_order_amount, app.user.currency ?? null) %}
|
||||
{% set min_order_price = pricedetail_helper.calculateAvgPrice(part, min_order_amount, app.user.currency ?? null ) %}
|
||||
{% if max_order_price is not null %}
|
||||
<h6>
|
||||
<i class="fas fa-money-bill-alt fa-fw"></i>
|
||||
<span class="text-muted">
|
||||
<span title="{% trans %}part.avg_price.label{% endtrans %} {{ max_order_amount | format_amount(part.partUnit) }}">{{ max_order_price | format_money(app.user.currency ?? null) }}</span>
|
||||
{% if min_order_amount < max_order_amount %}
|
||||
{% if min_order_price is not null and min_order_amount < max_order_amount %}
|
||||
<span> - </span>
|
||||
<span title="{% trans %}part.avg_price.label{% endtrans %} {{ min_order_amount | format_amount(part.partUnit) }}">{{pricedetail_helper.calculateAvgPrice(part, min_order_amount, app.user.currency ?? null ) | format_money(app.user.currency ?? null) }}</span>
|
||||
<span title="{% trans %}part.avg_price.label{% endtrans %} {{ min_order_amount | format_amount(part.partUnit) }}">{% if max_order_price is not null %}{{ min_order_price | format_money(app.user.currency ?? null) }}{% else %}???{% endif %}</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</h6>
|
||||
|
||||
Reference in New Issue
Block a user