Set purchase date and price
This commit is contained in:
parent
195f18f141
commit
f0cec23da9
@ -168,6 +168,15 @@
|
|||||||
# Default: 3333
|
# Default: 3333
|
||||||
# BK_PORT=3333
|
# BK_PORT=3333
|
||||||
|
|
||||||
|
# Optional: Format of the timestamp for purchase dates
|
||||||
|
# Check https://docs.python.org/3/library/time.html#time.strftime for format details
|
||||||
|
# Default: %d/%m/%Y
|
||||||
|
# BK_PURCHASE_DATE_FORMAT=%m/%d/%Y
|
||||||
|
|
||||||
|
# Optional: Currency to display for purchase prices.
|
||||||
|
# Default: €
|
||||||
|
# BK_PURCHASE_CURRENCY=£
|
||||||
|
|
||||||
# Optional: Change the default order of purchase locations. By default ordered by insertion order.
|
# Optional: Change the default order of purchase locations. By default ordered by insertion order.
|
||||||
# Useful column names for this option are:
|
# Useful column names for this option are:
|
||||||
# - "bricktracker_metadata_purchase_locations"."name" ASC: storage name
|
# - "bricktracker_metadata_purchase_locations"."name" ASC: storage name
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
- Added: `BK_HIDE_ALL_STORAGES`, hide the "Storages" menu entry
|
- Added: `BK_HIDE_ALL_STORAGES`, hide the "Storages" menu entry
|
||||||
- Added: `BK_STORAGE_DEFAULT_ORDER`, ordering of storages
|
- Added: `BK_STORAGE_DEFAULT_ORDER`, ordering of storages
|
||||||
- Added: `BK_PURCHASE_LOCATION_DEFAULT_ORDER`, ordering of purchase locations
|
- Added: `BK_PURCHASE_LOCATION_DEFAULT_ORDER`, ordering of purchase locations
|
||||||
|
- Added: `BK_PURCHASE_CURRENCY`, currency to display for purchase prices
|
||||||
|
- Added: `BK_PURCHASE_DATE_FORMAT`, date format for purchase dates
|
||||||
|
|
||||||
### Code
|
### Code
|
||||||
|
|
||||||
@ -40,7 +42,7 @@
|
|||||||
- Ownership
|
- Ownership
|
||||||
- Tags
|
- Tags
|
||||||
- Storage
|
- Storage
|
||||||
- Purchase location
|
- Purchase location, date, price
|
||||||
|
|
||||||
- Storage
|
- Storage
|
||||||
- Storage content and list
|
- Storage content and list
|
||||||
@ -87,7 +89,7 @@
|
|||||||
- Tags
|
- Tags
|
||||||
- Refresh
|
- Refresh
|
||||||
- Storage
|
- Storage
|
||||||
- Purchase location
|
- Purchase location, date, price
|
||||||
|
|
||||||
- Sets grid
|
- Sets grid
|
||||||
- Collapsible controls depending on screen size
|
- Collapsible controls depending on screen size
|
||||||
|
@ -41,6 +41,8 @@ CONFIG: Final[list[dict[str, Any]]] = [
|
|||||||
{'n': 'PARTS_DEFAULT_ORDER', 'd': '"rebrickable_parts"."name" ASC, "rebrickable_parts"."color_name" ASC, "bricktracker_parts"."spare" ASC'}, # noqa: E501
|
{'n': 'PARTS_DEFAULT_ORDER', 'd': '"rebrickable_parts"."name" ASC, "rebrickable_parts"."color_name" ASC, "bricktracker_parts"."spare" ASC'}, # noqa: E501
|
||||||
{'n': 'PARTS_FOLDER', 'd': 'parts', 's': True},
|
{'n': 'PARTS_FOLDER', 'd': 'parts', 's': True},
|
||||||
{'n': 'PORT', 'd': 3333, 'c': int},
|
{'n': 'PORT', 'd': 3333, 'c': int},
|
||||||
|
{'n': 'PURCHASE_DATE_FORMAT', 'd': '%d/%m/%Y'},
|
||||||
|
{'n': 'PURCHASE_CURRENCY', 'd': '€'},
|
||||||
{'n': 'PURCHASE_LOCATION_DEFAULT_ORDER', 'd': '"bricktracker_metadata_purchase_locations"."name" ASC'}, # noqa: E501
|
{'n': 'PURCHASE_LOCATION_DEFAULT_ORDER', 'd': '"bricktracker_metadata_purchase_locations"."name" ASC'}, # noqa: E501
|
||||||
{'n': 'RANDOM', 'e': 'RANDOM', 'c': bool},
|
{'n': 'RANDOM', 'e': 'RANDOM', 'c': bool},
|
||||||
{'n': 'REBRICKABLE_API_KEY', 'e': 'REBRICKABLE_API_KEY', 'd': ''},
|
{'n': 'REBRICKABLE_API_KEY', 'e': 'REBRICKABLE_API_KEY', 'd': ''},
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from datetime import datetime
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Any, Self, TYPE_CHECKING
|
from typing import Any, Self, TYPE_CHECKING
|
||||||
@ -5,7 +6,7 @@ from uuid import uuid4
|
|||||||
|
|
||||||
from flask import current_app, url_for
|
from flask import current_app, url_for
|
||||||
|
|
||||||
from .exceptions import NotFoundException
|
from .exceptions import NotFoundException, DatabaseException, ErrorException
|
||||||
from .minifigure_list import BrickMinifigureList
|
from .minifigure_list import BrickMinifigureList
|
||||||
from .part_list import BrickPartList
|
from .part_list import BrickPartList
|
||||||
from .rebrickable_set import RebrickableSet
|
from .rebrickable_set import RebrickableSet
|
||||||
@ -27,6 +28,8 @@ class BrickSet(RebrickableSet):
|
|||||||
select_query: str = 'set/select/full'
|
select_query: str = 'set/select/full'
|
||||||
light_query: str = 'set/select/light'
|
light_query: str = 'set/select/light'
|
||||||
insert_query: str = 'set/insert'
|
insert_query: str = 'set/insert'
|
||||||
|
update_purchase_date_query: str = 'set/update/purchase_date'
|
||||||
|
update_purchase_price_query: str = 'set/update/purchase_price'
|
||||||
|
|
||||||
# Delete a set
|
# Delete a set
|
||||||
def delete(self, /) -> None:
|
def delete(self, /) -> None:
|
||||||
@ -152,6 +155,30 @@ class BrickSet(RebrickableSet):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# Purchase date
|
||||||
|
def purchase_date(self, /, *, standard: bool = False) -> str:
|
||||||
|
if self.fields.purchase_date is not None:
|
||||||
|
time = datetime.fromtimestamp(self.fields.purchase_date)
|
||||||
|
|
||||||
|
if standard:
|
||||||
|
return time.strftime('%Y/%m/%d')
|
||||||
|
else:
|
||||||
|
return time.strftime(
|
||||||
|
current_app.config['PURCHASE_DATE_FORMAT']
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
# Purchase price with currency
|
||||||
|
def purchase_price(self, /) -> str:
|
||||||
|
if self.fields.purchase_price is not None:
|
||||||
|
return '{price}{currency}'.format(
|
||||||
|
price=self.fields.purchase_price,
|
||||||
|
currency=current_app.config['PURCHASE_CURRENCY']
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
# Minifigures
|
# Minifigures
|
||||||
def minifigures(self, /) -> BrickMinifigureList:
|
def minifigures(self, /) -> BrickMinifigureList:
|
||||||
return BrickMinifigureList().from_set(self)
|
return BrickMinifigureList().from_set(self)
|
||||||
@ -194,6 +221,80 @@ class BrickSet(RebrickableSet):
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
# Update the purchase date
|
||||||
|
def update_purchase_date(self, json: Any | None, /) -> Any:
|
||||||
|
value = json.get('value', None) # type: ignore
|
||||||
|
|
||||||
|
try:
|
||||||
|
if value == '':
|
||||||
|
value = None
|
||||||
|
|
||||||
|
if value is not None:
|
||||||
|
value = datetime.strptime(value, '%Y/%m/%d').timestamp()
|
||||||
|
except Exception:
|
||||||
|
raise ErrorException('{value} is not a date'.format(
|
||||||
|
value=value,
|
||||||
|
))
|
||||||
|
|
||||||
|
self.fields.purchase_date = value
|
||||||
|
|
||||||
|
rows, _ = BrickSQL().execute_and_commit(
|
||||||
|
self.update_purchase_date_query,
|
||||||
|
parameters=self.sql_parameters()
|
||||||
|
)
|
||||||
|
|
||||||
|
if rows != 1:
|
||||||
|
raise DatabaseException('Could not update the purchase date for set {set} ({id})'.format( # noqa: E501
|
||||||
|
set=self.fields.set,
|
||||||
|
id=self.fields.id,
|
||||||
|
))
|
||||||
|
|
||||||
|
# Info
|
||||||
|
logger.info('Purchase date changed to "{value}" for set {set} ({id})'.format( # noqa: E501
|
||||||
|
value=value,
|
||||||
|
set=self.fields.set,
|
||||||
|
id=self.fields.id,
|
||||||
|
))
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
# Update the purchase price
|
||||||
|
def update_purchase_price(self, json: Any | None, /) -> Any:
|
||||||
|
value = json.get('value', None) # type: ignore
|
||||||
|
|
||||||
|
try:
|
||||||
|
if value == '':
|
||||||
|
value = None
|
||||||
|
|
||||||
|
if value is not None:
|
||||||
|
value = float(value)
|
||||||
|
except Exception:
|
||||||
|
raise ErrorException('{value} is not a number or empty'.format(
|
||||||
|
value=value,
|
||||||
|
))
|
||||||
|
|
||||||
|
self.fields.purchase_price = value
|
||||||
|
|
||||||
|
rows, _ = BrickSQL().execute_and_commit(
|
||||||
|
self.update_purchase_price_query,
|
||||||
|
parameters=self.sql_parameters()
|
||||||
|
)
|
||||||
|
|
||||||
|
if rows != 1:
|
||||||
|
raise DatabaseException('Could not update the purchase price for set {set} ({id})'.format( # noqa: E501
|
||||||
|
set=self.fields.set,
|
||||||
|
id=self.fields.id,
|
||||||
|
))
|
||||||
|
|
||||||
|
# Info
|
||||||
|
logger.info('Purchase price changed to "{value}" for set {set} ({id})'.format( # noqa: E501
|
||||||
|
value=value,
|
||||||
|
set=self.fields.set,
|
||||||
|
id=self.fields.id,
|
||||||
|
))
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
# Self url
|
# Self url
|
||||||
def url(self, /) -> str:
|
def url(self, /) -> str:
|
||||||
return url_for('set.details', id=self.fields.id)
|
return url_for('set.details', id=self.fields.id)
|
||||||
@ -230,3 +331,11 @@ class BrickSet(RebrickableSet):
|
|||||||
return url_for('storage.details', id=self.fields.storage)
|
return url_for('storage.details', id=self.fields.storage)
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
# Update purchase date url
|
||||||
|
def url_for_purchase_date(self, /) -> str:
|
||||||
|
return url_for('set.update_purchase_date', id=self.fields.id)
|
||||||
|
|
||||||
|
# Update purchase price url
|
||||||
|
def url_for_purchase_price(self, /) -> str:
|
||||||
|
return url_for('set.update_purchase_price', id=self.fields.id)
|
||||||
|
@ -27,7 +27,7 @@ CREATE TABLE "bricktracker_sets" (
|
|||||||
"set" TEXT NOT NULL,
|
"set" TEXT NOT NULL,
|
||||||
"description" TEXT,
|
"description" TEXT,
|
||||||
"storage" TEXT, -- Storage bin location
|
"storage" TEXT, -- Storage bin location
|
||||||
"purchase_date" INTEGER, -- Purchase data
|
"purchase_date" REAL, -- Purchase data
|
||||||
"purchase_location" TEXT, -- Purchase location
|
"purchase_location" TEXT, -- Purchase location
|
||||||
"purchase_price" REAL, -- Purchase price
|
"purchase_price" REAL, -- Purchase price
|
||||||
PRIMARY KEY("id"),
|
PRIMARY KEY("id"),
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
SELECT
|
SELECT
|
||||||
{% block id %}{% endblock %}
|
{% block id %}{% endblock %}
|
||||||
"bricktracker_sets"."storage",
|
"bricktracker_sets"."storage",
|
||||||
|
"bricktracker_sets"."purchase_date",
|
||||||
"bricktracker_sets"."purchase_location",
|
"bricktracker_sets"."purchase_location",
|
||||||
|
"bricktracker_sets"."purchase_price",
|
||||||
"rebrickable_sets"."set",
|
"rebrickable_sets"."set",
|
||||||
"rebrickable_sets"."number",
|
"rebrickable_sets"."number",
|
||||||
"rebrickable_sets"."version",
|
"rebrickable_sets"."version",
|
||||||
|
3
bricktracker/sql/set/update/purchase_date.sql
Normal file
3
bricktracker/sql/set/update/purchase_date.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
UPDATE "bricktracker_sets"
|
||||||
|
SET "purchase_date" = :purchase_date
|
||||||
|
WHERE "bricktracker_sets"."id" IS NOT DISTINCT FROM :id
|
3
bricktracker/sql/set/update/purchase_price.sql
Normal file
3
bricktracker/sql/set/update/purchase_price.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
UPDATE "bricktracker_sets"
|
||||||
|
SET "purchase_price" = :purchase_price
|
||||||
|
WHERE "bricktracker_sets"."id" IS NOT DISTINCT FROM :id
|
@ -41,6 +41,18 @@ def list() -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Change the value of purchase date
|
||||||
|
@set_page.route('/<id>/purchase_date', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__, json=True)
|
||||||
|
def update_purchase_date(*, id: str) -> Response:
|
||||||
|
brickset = BrickSet().select_light(id)
|
||||||
|
|
||||||
|
value = brickset.update_purchase_date(request.json)
|
||||||
|
|
||||||
|
return jsonify({'value': value})
|
||||||
|
|
||||||
|
|
||||||
# Change the value of purchase location
|
# Change the value of purchase location
|
||||||
@set_page.route('/<id>/purchase_location', methods=['POST'])
|
@set_page.route('/<id>/purchase_location', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@ -60,6 +72,18 @@ def update_purchase_location(*, id: str) -> Response:
|
|||||||
return jsonify({'value': value})
|
return jsonify({'value': value})
|
||||||
|
|
||||||
|
|
||||||
|
# Change the value of purchase price
|
||||||
|
@set_page.route('/<id>/purchase_price', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__, json=True)
|
||||||
|
def update_purchase_price(*, id: str) -> Response:
|
||||||
|
brickset = BrickSet().select_light(id)
|
||||||
|
|
||||||
|
value = brickset.update_purchase_price(request.json)
|
||||||
|
|
||||||
|
return jsonify({'value': value})
|
||||||
|
|
||||||
|
|
||||||
# Change the state of a owner
|
# Change the state of a owner
|
||||||
@set_page.route('/<id>/owner/<metadata_id>', methods=['POST'])
|
@set_page.route('/<id>/owner/<metadata_id>', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -21,6 +21,7 @@ It also uses the following libraries and frameworks:
|
|||||||
- `tinysort` (https://github.com/Sjeiti/TinySort)
|
- `tinysort` (https://github.com/Sjeiti/TinySort)
|
||||||
- `sortable` (https://github.com/tofsjonas/sortable)
|
- `sortable` (https://github.com/tofsjonas/sortable)
|
||||||
- `simple-datatables` (https://github.com/fiduswriter/simple-datatables)
|
- `simple-datatables` (https://github.com/fiduswriter/simple-datatables)
|
||||||
|
- `vanillajs-datepicker` (https://github.com/mymth/vanillajs-datepicker)
|
||||||
|
|
||||||
The BrickTracker brick logo is part of the Small n' Flat Icons set designed by [Arnaud Chesne](https://iconduck.com/designers/arnaud-chesne).
|
The BrickTracker brick logo is part of the Small n' Flat Icons set designed by [Arnaud Chesne](https://iconduck.com/designers/arnaud-chesne).
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// Generic state changer with visual feedback
|
// Generic state changer with visual feedback
|
||||||
// Tooltips require boostrap.Tooltip
|
// Tooltips requires boostrap.Tooltip
|
||||||
|
// Date requires vanillajs-datepicker
|
||||||
class BrickChanger {
|
class BrickChanger {
|
||||||
constructor(prefix, id, url, parent = undefined) {
|
constructor(prefix, id, url, parent = undefined) {
|
||||||
this.prefix = prefix
|
this.prefix = prefix
|
||||||
@ -51,6 +52,20 @@ class BrickChanger {
|
|||||||
changer.change();
|
changer.change();
|
||||||
})(this));
|
})(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Date picker
|
||||||
|
this.picker = undefined;
|
||||||
|
if (this.html_element.dataset.changerDate == "true") {
|
||||||
|
this.picker = new Datepicker(this.html_element, {
|
||||||
|
buttonClass: 'btn',
|
||||||
|
format: 'yyyy/mm/dd',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Picker fires a custom "changeDate" event
|
||||||
|
this.html_element.addEventListener("changeDate", ((changer) => (e) => {
|
||||||
|
changer.change();
|
||||||
|
})(this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean the status
|
// Clean the status
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.12.0/baguetteBox.css" integrity="sha512-VZ783G3QIpxXpg7tWpzHn+XhjsOCIxFYoSWmyipKCB41OYaB9i4brxAWuY1c8gGCSqKo7uvckzPJhYcdBZQ9gg==" crossorigin="anonymous" referrerpolicy="no-referrer">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.12.0/baguetteBox.css" integrity="sha512-VZ783G3QIpxXpg7tWpzHn+XhjsOCIxFYoSWmyipKCB41OYaB9i4brxAWuY1c8gGCSqKo7uvckzPJhYcdBZQ9gg==" crossorigin="anonymous" referrerpolicy="no-referrer">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/simple-datatables@9.2.1/dist/style.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/simple-datatables@9.2.1/dist/style.min.css">
|
||||||
<link href="https://cdn.jsdelivr.net/npm/remixicon@4.6.0/fonts/remixicon.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/remixicon@4.6.0/fonts/remixicon.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vanillajs-datepicker@1.3.4/dist/css/datepicker-bs5.min.css">
|
||||||
<link href="{{ url_for('static', filename='styles.css') }}" rel="stylesheet">
|
<link href="{{ url_for('static', filename='styles.css') }}" rel="stylesheet">
|
||||||
<link rel="icon" type="image/png" sizes="48x48" href="{{ url_for('static', filename='brick.png') }}">
|
<link rel="icon" type="image/png" sizes="48x48" href="{{ url_for('static', filename='brick.png') }}">
|
||||||
</head>
|
</head>
|
||||||
@ -78,6 +79,7 @@
|
|||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.12.0/baguetteBox.min.js" integrity="sha512-HzIuiABxntLbBS8ClRa7drXZI3cqvkAZ5DD0JCAkmRwUtykSGqzA9uItHivDhRUYnW3MMyY5xqk7qVUHOEMbMA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.12.0/baguetteBox.min.js" integrity="sha512-HzIuiABxntLbBS8ClRa7drXZI3cqvkAZ5DD0JCAkmRwUtykSGqzA9uItHivDhRUYnW3MMyY5xqk7qVUHOEMbMA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.8.1/socket.io.min.js" integrity="sha512-8ExARjWWkIllMlNzVg7JKq9RKWPlJABQUNq6YvAjE/HobctjH/NA+bSiDMDvouBVjp4Wwnf1VP1OEv7Zgjtuxw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.8.1/socket.io.min.js" integrity="sha512-8ExARjWWkIllMlNzVg7JKq9RKWPlJABQUNq6YvAjE/HobctjH/NA+bSiDMDvouBVjp4Wwnf1VP1OEv7Zgjtuxw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/simple-datatables@9.2.1/dist/umd/simple-datatables.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/simple-datatables@9.2.1/dist/umd/simple-datatables.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/vanillajs-datepicker@1.3.4/dist/js/datepicker-full.min.js"></script>
|
||||||
<!-- BrickTracker scripts -->
|
<!-- BrickTracker scripts -->
|
||||||
<script src="{{ url_for('static', filename='scripts/changer.js') }}"></script>
|
<script src="{{ url_for('static', filename='scripts/changer.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='scripts/grid/filter.js') }}"></script>
|
<script src="{{ url_for('static', filename='scripts/grid/filter.js') }}"></script>
|
||||||
|
@ -65,6 +65,15 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro purchase_date(date, solo=false, last=false) %}
|
||||||
|
{% if last %}
|
||||||
|
{% set tooltip=date %}
|
||||||
|
{% else %}
|
||||||
|
{% set text=date %}
|
||||||
|
{% endif %}
|
||||||
|
{{ badge(check=date, solo=solo, last=last, color='light border', icon='calendar-line', text=text, tooltip=tooltip, collapsible='Date:') }}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro purchase_location(item, purchase_locations, solo=false, last=false) %}
|
{% macro purchase_location(item, purchase_locations, solo=false, last=false) %}
|
||||||
{% if purchase_locations and item.fields.purchase_location in purchase_locations.mapping %}
|
{% if purchase_locations and item.fields.purchase_location in purchase_locations.mapping %}
|
||||||
{% set purchase_location = purchase_locations.mapping[item.fields.purchase_location] %}
|
{% set purchase_location = purchase_locations.mapping[item.fields.purchase_location] %}
|
||||||
@ -73,10 +82,19 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
{% set text=purchase_location.fields.name %}
|
{% set text=purchase_location.fields.name %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ badge(check=purchase_location, solo=solo, last=last, color='light border', icon='building-line', text=text, tooltip=tooltip) }}
|
{{ badge(check=purchase_location, solo=solo, last=last, color='light border', icon='building-line', text=text, tooltip=tooltip, collapsible='Location:') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro purchase_price(price, solo=false, last=false) %}
|
||||||
|
{% if last %}
|
||||||
|
{% set tooltip=price %}
|
||||||
|
{% else %}
|
||||||
|
{% set text=price %}
|
||||||
|
{% endif %}
|
||||||
|
{{ badge(check=price, solo=solo, last=last, color='light border', icon='wallet-3-line', text=text, tooltip=tooltip, collapsible='Price:') }}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro set(set, solo=false, last=false, url=None, id=None) %}
|
{% macro set(set, solo=false, last=false, url=None, id=None) %}
|
||||||
{% if id %}
|
{% if id %}
|
||||||
{% set url=url_for('set.details', id=id) %}
|
{% set url=url_for('set.details', id=id) %}
|
||||||
|
@ -17,19 +17,22 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro input(name, id, prefix, url, value, all=none, read_only=none) %}
|
{% macro input(name, id, prefix, url, value, all=none, read_only=none, icon=none, suffix=none, date=false) %}
|
||||||
{% if all or read_only %}
|
{% if all or read_only %}
|
||||||
{{ value }}
|
{{ value }}
|
||||||
{% else %}
|
{% else %}
|
||||||
<label class="visually-hidden" for="{{ prefix }}-{{ id }}">{{ name }}</label>
|
<label class="visually-hidden" for="{{ prefix }}-{{ id }}">{{ name }}</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
{% if icon %}<span class="input-group-text"><i class="ri-{{ icon }} me-1"></i><span class="ms-1 d-none d-md-inline"> {{ name }}</span></span>{% endif %}
|
||||||
<input class="form-control form-control-sm flex-shrink-1" type="text" id="{{ prefix }}-{{ id }}" value="{% if value %}{{ value }}{% endif %}"
|
<input class="form-control form-control-sm flex-shrink-1" type="text" id="{{ prefix }}-{{ id }}" value="{% if value %}{{ value }}{% endif %}"
|
||||||
{% if g.login.is_authenticated() %}
|
{% if g.login.is_authenticated() %}
|
||||||
data-changer-id="{{ id }}" data-changer-prefix="{{ prefix }}" data-changer-url="{{ url }}"
|
data-changer-id="{{ id }}" data-changer-prefix="{{ prefix }}" data-changer-url="{{ url }}"
|
||||||
|
{% if date %}data-changer-date="true"{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
disabled
|
disabled
|
||||||
{% endif %}
|
{% endif %}
|
||||||
autocomplete="off">
|
autocomplete="off">
|
||||||
|
{% if suffix %}<span class="input-group-text d-none d-md-inline">{{ suffix }}</span>{% endif %}
|
||||||
{% if g.login.is_authenticated() %}
|
{% if g.login.is_authenticated() %}
|
||||||
<span id="status-{{ prefix }}-{{ id }}" class="input-group-text ri-save-line"></span>
|
<span id="status-{{ prefix }}-{{ id }}" class="input-group-text ri-save-line"></span>
|
||||||
<button id="clear-{{ prefix }}-{{ id }}" type="button" class="btn btn-sm btn-light btn-outline-danger border"><i class="ri-eraser-line"></i></button>
|
<button id="clear-{{ prefix }}-{{ id }}" type="button" class="btn btn-sm btn-light btn-outline-danger border"><i class="ri-eraser-line"></i></button>
|
||||||
@ -45,7 +48,7 @@
|
|||||||
{% set prefix=metadata_list.as_prefix() %}
|
{% set prefix=metadata_list.as_prefix() %}
|
||||||
<label class="visually-hidden" for="{{ prefix }}-{{ item.fields.id }}">{{ name }}</label>
|
<label class="visually-hidden" for="{{ prefix }}-{{ item.fields.id }}">{{ name }}</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
{% if icon %}<span class="input-group-text"><i class="ri-{{ icon }}"></i></span>{% endif %}
|
{% if icon %}<span class="input-group-text"><i class="ri-{{ icon }} me-1"></i><span class="ms-1 d-none d-md-inline"> {{ name }}</span></span>{% endif %}
|
||||||
<select id="{{ prefix }}-{{ item.fields.id }}" class="form-select"
|
<select id="{{ prefix }}-{{ item.fields.id }}" class="form-select"
|
||||||
{% if not delete %}
|
{% if not delete %}
|
||||||
data-changer-id="{{ item.fields.id }}" data-changer-prefix="{{ prefix }}" data-changer-url="{{ metadata_list.url_for_set_value(item.fields.id) }}"
|
data-changer-id="{{ item.fields.id }}" data-changer-prefix="{{ prefix }}" data-changer-url="{{ metadata_list.url_for_set_value(item.fields.id) }}"
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
{% if not config['HIDE_TABLE_DAMAGED_PARTS'] %}
|
{% if not config['HIDE_TABLE_DAMAGED_PARTS'] %}
|
||||||
data-has-damaged="{{ (item.fields.total_damaged > 0) | int }}" data-damaged="{{ item.fields.total_damaged }}"
|
data-has-damaged="{{ (item.fields.total_damaged > 0) | int }}" data-damaged="{{ item.fields.total_damaged }}"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if item.fields.purchase_date is not none %}data-purchase-date="{{ item.fields.purchase_date }}"{% endif %}
|
||||||
|
{% if item.fields.purchase_price is not none %}data-purchase-price="{{ item.fields.purchase_price }}"{% endif %}
|
||||||
data-has-purchase-location="{{ item.fields.purchase_location is not none | int }}"
|
data-has-purchase-location="{{ item.fields.purchase_location is not none | int }}"
|
||||||
{% if item.fields.purchase_location is not none %}
|
{% if item.fields.purchase_location is not none %}
|
||||||
data-purchase-location="{{ item.fields.purchase_location }}"
|
data-purchase-location="{{ item.fields.purchase_location }}"
|
||||||
@ -68,8 +70,10 @@
|
|||||||
{{ badge.owner(item, owner, solo=solo, last=last) }}
|
{{ badge.owner(item, owner, solo=solo, last=last) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{{ badge.storage(item, brickset_storages, solo=solo, last=last) }}
|
{{ badge.storage(item, brickset_storages, solo=solo, last=last) }}
|
||||||
{{ badge.purchase_location(item, brickset_purchase_locations, solo=solo, last=last) }}
|
|
||||||
{% if not last %}
|
{% if not last %}
|
||||||
|
{{ badge.purchase_date(item.purchase_date(), solo=solo, last=last) }}
|
||||||
|
{{ badge.purchase_location(item, brickset_purchase_locations, solo=solo, last=last) }}
|
||||||
|
{{ badge.purchase_price(item.purchase_price(), solo=solo, last=last) }}
|
||||||
{% if not solo %}
|
{% if not solo %}
|
||||||
{{ badge.instructions(item, solo=solo, last=last) }}
|
{{ badge.instructions(item, solo=solo, last=last) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{% if g.login.is_authenticated() %}
|
{% if g.login.is_authenticated() %}
|
||||||
{{ accordion.header('Management', 'set-management', 'set-details', icon='settings-4-line', class='p-0') }}
|
{{ accordion.header('Management', 'set-management', 'set-details', icon='settings-4-line', class='p-0', expanded=true) }}
|
||||||
{{ accordion.header('Owners', 'owner', 'set-management', icon='group-line', class='p-0') }}
|
{{ accordion.header('Owners', 'owner', 'set-management', icon='group-line', class='p-0') }}
|
||||||
<ul class="list-group list-group-flush">
|
<ul class="list-group list-group-flush">
|
||||||
{% if brickset_owners | length %}
|
{% if brickset_owners | length %}
|
||||||
@ -14,12 +14,23 @@
|
|||||||
<a class="list-group-item list-group-item-action" href="{{ url_for('admin.admin', open_owner=true) }}"><i class="ri-settings-4-line"></i> Manage the set owners</a>
|
<a class="list-group-item list-group-item-action" href="{{ url_for('admin.admin', open_owner=true) }}"><i class="ri-settings-4-line"></i> Manage the set owners</a>
|
||||||
</div>
|
</div>
|
||||||
{{ accordion.footer() }}
|
{{ accordion.footer() }}
|
||||||
{{ accordion.header('Purchase location', 'purchase-location', 'set-management', icon='building-line') }}
|
{{ accordion.header('Purchase', 'purchase', 'set-management', icon='wallet-3-line', expanded=true) }}
|
||||||
{% if brickset_purchase_locations | length %}
|
<div class="alert alert-info" role="alert">The expected date format here is <code>yyyy/mm/dd</code> (year/month/day), but you can configured how it is displayed in the set card with the <code>PURCHASE_DATE_FORMAT</code> variable.</div>
|
||||||
{{ form.select('Purchase location', item, 'purchase_location', brickset_purchase_locations, delete=delete) }}
|
<div class="row row-cols-lg-auto g-1 justify-content-start align-items-center pb-2">
|
||||||
{% else %}
|
<div class="col-12">
|
||||||
<p class="text-center"><i class="ri-error-warning-line"></i> No purchase location found.</p>
|
{{ form.input('Date', item.fields.id, 'date', item.url_for_purchase_date(), item.purchase_date(standard=true), icon='calendar-line', date=true) }}
|
||||||
{% endif %}
|
</div>
|
||||||
|
<div class="col-12 flex-grow-1">
|
||||||
|
{{ form.input('Price', item.fields.id, 'price', item.url_for_purchase_price(), item.fields.purchase_price, suffix=config['PURCHASE_CURRENCY'], icon='wallet-3-line') }}
|
||||||
|
</div>
|
||||||
|
<div class="col-12 flex-grow-1">
|
||||||
|
{% if brickset_purchase_locations | length %}
|
||||||
|
{{ form.select('Location', item, 'purchase_location', brickset_purchase_locations, icon='building-line', delete=delete) }}
|
||||||
|
{% else %}
|
||||||
|
<i class="ri-error-warning-line"></i> No purchase location found.
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<a href="{{ url_for('admin.admin', open_purchase_location=true) }}" class="btn btn-primary" role="button"><i class="ri-settings-4-line"></i> Manage the set purchase locations</a>
|
<a href="{{ url_for('admin.admin', open_purchase_location=true) }}" class="btn btn-primary" role="button"><i class="ri-settings-4-line"></i> Manage the set purchase locations</a>
|
||||||
{{ accordion.footer() }}
|
{{ accordion.footer() }}
|
||||||
|
@ -22,6 +22,10 @@
|
|||||||
<button id="sort-damaged" type="button" class="btn btn-outline-primary mb-2"
|
<button id="sort-damaged" type="button" class="btn btn-outline-primary mb-2"
|
||||||
data-sort-attribute="damaged" data-sort-desc="true"><i class="ri-error-warning-line"></i><span class="d-none d-xl-inline"> Damaged</span></button>
|
data-sort-attribute="damaged" data-sort-desc="true"><i class="ri-error-warning-line"></i><span class="d-none d-xl-inline"> Damaged</span></button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<button id="sort-purchase-date" type="button" class="btn btn-outline-primary mb-2"
|
||||||
|
data-sort-attribute="purchase-date" data-sort-desc="true"><i class="ri-calendar-line"></i><span class="d-none d-xl-inline"> Date</span></button>
|
||||||
|
<button id="sort-purchase-price" type="button" class="btn btn-outline-primary mb-2"
|
||||||
|
data-sort-attribute="purchase-price" data-sort-desc="true"><i class="ri-wallet-3-line"></i><span class="d-none d-xl-inline"> Price</span></button>
|
||||||
<button id="sort-clear" type="button" class="btn btn-outline-dark mb-2"
|
<button id="sort-clear" type="button" class="btn btn-outline-dark mb-2"
|
||||||
data-sort-clear="true"><i class="ri-close-circle-line"></i><span class="d-none d-xl-inline"> Clear</span></button>
|
data-sort-clear="true"><i class="ri-close-circle-line"></i><span class="d-none d-xl-inline"> Clear</span></button>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user