Update app.py
Browse files
app.py
CHANGED
|
@@ -1200,7 +1200,6 @@ def item_movement_report():
|
|
| 1200 |
html = BASE_TEMPLATE.replace('__TITLE__', "Движение товаров").replace('__CONTENT__', ITEM_MOVEMENT_CONTENT).replace('__SCRIPTS__', ITEM_MOVEMENT_SCRIPTS)
|
| 1201 |
return render_template_string(html, inventory=inventory, movements=movements, product_id=product_id, variant_id=variant_id, selected_product=selected_product, selected_variant=selected_variant)
|
| 1202 |
|
| 1203 |
-
|
| 1204 |
@app.route('/reports/product_roi')
|
| 1205 |
@admin_required
|
| 1206 |
def product_roi_report():
|
|
@@ -3197,9 +3196,7 @@ INVENTORY_CONTENT = """
|
|
| 3197 |
<div id="modal-scanner-add" class="mb-2" style="display:none;"></div>
|
| 3198 |
<hr>
|
| 3199 |
<h6>Варианты товара</h6>
|
| 3200 |
-
<div
|
| 3201 |
-
<div id="variants-container-add"></div>
|
| 3202 |
-
</div>
|
| 3203 |
<button type="button" class="btn btn-sm btn-outline-success mt-2" id="add-variant-btn-add">Добавить вариант</button>
|
| 3204 |
</div>
|
| 3205 |
<div class="modal-footer"><button type="submit" class="btn btn-primary">Сохранить</button></div>
|
|
@@ -3222,36 +3219,34 @@ INVENTORY_CONTENT = """
|
|
| 3222 |
</div>
|
| 3223 |
<hr>
|
| 3224 |
<h6>Варианты товара</h6>
|
| 3225 |
-
<div
|
| 3226 |
-
|
| 3227 |
-
|
| 3228 |
-
|
| 3229 |
-
|
| 3230 |
-
|
| 3231 |
-
|
| 3232 |
-
|
| 3233 |
-
|
| 3234 |
-
|
| 3235 |
-
|
| 3236 |
-
|
| 3237 |
-
|
| 3238 |
-
|
| 3239 |
-
|
| 3240 |
-
|
| 3241 |
-
|
| 3242 |
-
|
| 3243 |
-
|
| 3244 |
-
|
| 3245 |
-
|
| 3246 |
-
|
| 3247 |
-
|
| 3248 |
-
|
| 3249 |
-
|
| 3250 |
-
|
| 3251 |
-
</div>
|
| 3252 |
-
</div>
|
| 3253 |
-
{% endfor %}
|
| 3254 |
</div>
|
|
|
|
| 3255 |
</div>
|
| 3256 |
<button type="button" class="btn btn-sm btn-outline-success mt-2 add-variant-btn-edit" data-target-container="variants-container-edit-{{ p.id }}">Добавить вариант</button>
|
| 3257 |
</div>
|
|
@@ -3359,7 +3354,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 3359 |
let currentScanner = null;
|
| 3360 |
let currentScannerContainer = null;
|
| 3361 |
const placeholderImg = "{{ url_for('static', filename='placeholder.png') }}";
|
| 3362 |
-
const inventoryData =
|
| 3363 |
const isAdmin = {{ 'true' if session.admin_logged_in else 'false' }};
|
| 3364 |
|
| 3365 |
function startScannerFor(containerId, inputElement, callback) {
|
|
@@ -3561,8 +3556,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 3561 |
</div>
|
| 3562 |
</div>
|
| 3563 |
`;
|
| 3564 |
-
div.querySelector('.remove-variant-btn').addEventListener('click', () => div.remove());
|
| 3565 |
-
div.querySelector('.variant-image-upload').addEventListener('change', (e) => handleImageUpload(e.target));
|
| 3566 |
return div;
|
| 3567 |
};
|
| 3568 |
|
|
@@ -3572,20 +3565,22 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 3572 |
}
|
| 3573 |
});
|
| 3574 |
|
| 3575 |
-
document.getElementById('add-variant-btn-add').addEventListener('click', () => {
|
| 3576 |
-
document.getElementById('variants-container-add').appendChild(createVariantRow());
|
| 3577 |
-
});
|
| 3578 |
-
|
| 3579 |
-
document.querySelectorAll('.add-variant-btn-edit').forEach(btn => {
|
| 3580 |
-
btn.addEventListener('click', (e) => {
|
| 3581 |
-
const containerId = e.target.dataset.targetContainer;
|
| 3582 |
-
document.getElementById(containerId).appendChild(createVariantRow());
|
| 3583 |
-
});
|
| 3584 |
-
});
|
| 3585 |
-
|
| 3586 |
document.body.addEventListener('click', e => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3587 |
if (e.target.closest('.remove-variant-btn')) {
|
| 3588 |
e.target.closest('.variant-row').remove();
|
|
|
|
| 3589 |
}
|
| 3590 |
});
|
| 3591 |
|
|
@@ -4069,7 +4064,7 @@ ITEM_MOVEMENT_CONTENT = """
|
|
| 4069 |
ITEM_MOVEMENT_SCRIPTS = """
|
| 4070 |
<script>
|
| 4071 |
document.addEventListener('DOMContentLoaded', () => {
|
| 4072 |
-
const inventoryData =
|
| 4073 |
const searchInput = document.getElementById('movement-search');
|
| 4074 |
const searchResults = document.getElementById('movement-search-results');
|
| 4075 |
const productIdInput = document.getElementById('movement-product-id');
|
|
|
|
| 1200 |
html = BASE_TEMPLATE.replace('__TITLE__', "Движение товаров").replace('__CONTENT__', ITEM_MOVEMENT_CONTENT).replace('__SCRIPTS__', ITEM_MOVEMENT_SCRIPTS)
|
| 1201 |
return render_template_string(html, inventory=inventory, movements=movements, product_id=product_id, variant_id=variant_id, selected_product=selected_product, selected_variant=selected_variant)
|
| 1202 |
|
|
|
|
| 1203 |
@app.route('/reports/product_roi')
|
| 1204 |
@admin_required
|
| 1205 |
def product_roi_report():
|
|
|
|
| 3196 |
<div id="modal-scanner-add" class="mb-2" style="display:none;"></div>
|
| 3197 |
<hr>
|
| 3198 |
<h6>Варианты товара</h6>
|
| 3199 |
+
<div id="variants-container-add"></div>
|
|
|
|
|
|
|
| 3200 |
<button type="button" class="btn btn-sm btn-outline-success mt-2" id="add-variant-btn-add">Добавить вариант</button>
|
| 3201 |
</div>
|
| 3202 |
<div class="modal-footer"><button type="submit" class="btn btn-primary">Сохранить</button></div>
|
|
|
|
| 3219 |
</div>
|
| 3220 |
<hr>
|
| 3221 |
<h6>Варианты товара</h6>
|
| 3222 |
+
<div id="variants-container-edit-{{ p.id }}">
|
| 3223 |
+
{% for v in p.variants %}
|
| 3224 |
+
<div class="card mb-3 variant-row">
|
| 3225 |
+
<div class="card-body">
|
| 3226 |
+
<input type="hidden" name="variant_id[]" value="{{ v.id }}">
|
| 3227 |
+
<div class="row g-2 align-items-center">
|
| 3228 |
+
<div class="col-12 col-md-3">
|
| 3229 |
+
<img src="{{ v.image_url if v.image_url else url_for('static', filename='placeholder.png') }}" class="img-thumbnail variant-preview mb-1" style="width: 80px; height: 80px; object-fit: cover;">
|
| 3230 |
+
<input type="file" class="form-control form-control-sm variant-image-upload" accept="image/*">
|
| 3231 |
+
<input type="hidden" class="variant-image-url-input" name="variant_image_url[]" value="{{ v.image_url }}">
|
| 3232 |
+
</div>
|
| 3233 |
+
<div class="col-12 col-md-9">
|
| 3234 |
+
<div class="row g-2">
|
| 3235 |
+
<div class="col-12"><label>Название варианта</label><input type="text" name="variant_name[]" class="form-control" value="{{ v.option_value }}" required></div>
|
| 3236 |
+
<div class="col-12 col-sm-4"><label>Цена Общая</label><input type="text" name="variant_price_regular[]" class="form-control" value="{{ v.get('price_regular', v.get('price'))|string|replace('.', ',') }}" inputmode="decimal"></div>
|
| 3237 |
+
<div class="col-12 col-sm-4"><label>Цена Мин.</label><input type="text" name="variant_price_min[]" class="form-control" value="{{ v.get('price_min', '0.00')|string|replace('.', ',') }}" inputmode="decimal"></div>
|
| 3238 |
+
<div class="col-12 col-sm-4"><label>Цена Опт.</label><input type="text" name="variant_price_wholesale[]" class="form-control" value="{{ v.get('price_wholesale', '0.00')|string|replace('.', ',') }}" inputmode="decimal"></div>
|
| 3239 |
+
<div class="col-12 col-sm-6"><label>Себестоимость</label><input type="text" name="variant_cost_price[]" class="form-control" value="{{ v.cost_price|string|replace('.', ',') }}" inputmode="decimal"></div>
|
| 3240 |
+
<div class="col-12 col-sm-6"><label>Остаток</label><input type="number" name="variant_stock[]" class="form-control" value="{{ v.stock }}"></div>
|
| 3241 |
+
</div>
|
| 3242 |
+
</div>
|
| 3243 |
+
<div class="col-12 text-end">
|
| 3244 |
+
<button type="button" class="btn btn-sm btn-danger remove-variant-btn"><i class="fas fa-times"></i> Удалить вариант</button>
|
| 3245 |
+
</div>
|
| 3246 |
+
</div>
|
| 3247 |
+
</div>
|
|
|
|
|
|
|
|
|
|
| 3248 |
</div>
|
| 3249 |
+
{% endfor %}
|
| 3250 |
</div>
|
| 3251 |
<button type="button" class="btn btn-sm btn-outline-success mt-2 add-variant-btn-edit" data-target-container="variants-container-edit-{{ p.id }}">Добавить вариант</button>
|
| 3252 |
</div>
|
|
|
|
| 3354 |
let currentScanner = null;
|
| 3355 |
let currentScannerContainer = null;
|
| 3356 |
const placeholderImg = "{{ url_for('static', filename='placeholder.png') }}";
|
| 3357 |
+
const inventoryData = {{ inventory|tojson|safe }};
|
| 3358 |
const isAdmin = {{ 'true' if session.admin_logged_in else 'false' }};
|
| 3359 |
|
| 3360 |
function startScannerFor(containerId, inputElement, callback) {
|
|
|
|
| 3556 |
</div>
|
| 3557 |
</div>
|
| 3558 |
`;
|
|
|
|
|
|
|
| 3559 |
return div;
|
| 3560 |
};
|
| 3561 |
|
|
|
|
| 3565 |
}
|
| 3566 |
});
|
| 3567 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3568 |
document.body.addEventListener('click', e => {
|
| 3569 |
+
if (e.target.closest('#add-variant-btn-add')) {
|
| 3570 |
+
document.getElementById('variants-container-add').appendChild(createVariantRow());
|
| 3571 |
+
return;
|
| 3572 |
+
}
|
| 3573 |
+
|
| 3574 |
+
const editAddBtn = e.target.closest('.add-variant-btn-edit');
|
| 3575 |
+
if (editAddBtn) {
|
| 3576 |
+
const containerId = editAddBtn.dataset.targetContainer;
|
| 3577 |
+
document.getElementById(containerId).appendChild(createVariantRow());
|
| 3578 |
+
return;
|
| 3579 |
+
}
|
| 3580 |
+
|
| 3581 |
if (e.target.closest('.remove-variant-btn')) {
|
| 3582 |
e.target.closest('.variant-row').remove();
|
| 3583 |
+
return;
|
| 3584 |
}
|
| 3585 |
});
|
| 3586 |
|
|
|
|
| 4064 |
ITEM_MOVEMENT_SCRIPTS = """
|
| 4065 |
<script>
|
| 4066 |
document.addEventListener('DOMContentLoaded', () => {
|
| 4067 |
+
const inventoryData = {{ inventory|tojson|safe }};
|
| 4068 |
const searchInput = document.getElementById('movement-search');
|
| 4069 |
const searchResults = document.getElementById('movement-search-results');
|
| 4070 |
const productIdInput = document.getElementById('movement-product-id');
|