diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 656583e..879950d 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -43,7 +43,7 @@ jobs: uses: actions/checkout@v6 - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 diff --git a/README.md b/README.md index 2d9c446..5456654 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,7 @@ The main dashboard shows an overview of all your vehicles with key statistics: - Total fuel costs and consumption averages - Recent fuel logs and expenses - Upcoming reminders and overdue alerts +- Vehicle photo cards showing make/model/year and fuel type at a glance ### Vehicles Add and manage your vehicles with detailed information: @@ -158,6 +159,9 @@ Add and manage your vehicles with detailed information: - Fuel type and tank capacity - Custom specifications and notes - Photo upload support +- **Vehicle Sharing**: Mark a vehicle as "Shared" to make it visible and loggable by all users on the instance +- **Upcoming Maintenance**: Vehicle detail pages show a live panel of scheduled maintenance tasks, with overdue and due-soon alerts +- **Parts & Consumables**: Collapsible section on the vehicle page remembers your expand/collapse preference per vehicle ### Fuel Logs Track every fill-up with: @@ -169,12 +173,15 @@ Track every fill-up with: ### Expenses Categorize all vehicle-related costs: - Maintenance & Repairs +- Inspection (MOT, roadworthy checks) - Insurance - Tax & Registration - Parking & Tolls - Accessories - Other expenses +Record odometer readings alongside costs, and expand any expense row to see vendor and notes details inline. + ### Reminders Never miss important dates: - MOT/Inspection due dates @@ -202,9 +209,9 @@ Track regular payments: Store important vehicle documents: - Insurance certificates - Registration documents -- Service manuals +- Service manuals and instruction booklets (up to 300MB) - MOT certificates -- Any file type with expiry date tracking +- Any file type (PDF, images, Word, Excel, text, ePub) with expiry date tracking ### Fuel Stations Save your favorite stations: diff --git a/app/__init__.py b/app/__init__.py index 1b211dd..b647ba2 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -138,6 +138,8 @@ def _run_schema_migrations(app): ('tessie_battery_range', 'FLOAT'), ('tessie_last_updated', 'DATETIME'), ('tracking_unit', "VARCHAR(20) DEFAULT 'mileage'"), + ('annual_mileage_limit', 'FLOAT'), + ('annual_mileage_start_date', 'DATE'), ], 'users': [ ('date_format', "VARCHAR(20) DEFAULT 'DD/MM/YYYY'"), diff --git a/app/models.py b/app/models.py index 910620d..9c78306 100644 --- a/app/models.py +++ b/app/models.py @@ -105,12 +105,13 @@ def check_password(self, password): return check_password_hash(self.password_hash, password) def get_all_vehicles(self): - """Get all vehicles user has access to (owned + shared), sorted by make/model""" + """Get all vehicles user has access to (owned + explicitly shared + instance-shared), sorted by make/model""" owned = list(self.owned_vehicles.all()) shared = list(self.shared_vehicles) + instance_shared = Vehicle.query.filter_by(is_shared=True).all() seen = set() unique = [] - for v in owned + shared: + for v in owned + shared + instance_shared: if v.id not in seen: seen.add(v.id) unique.append(v) @@ -211,6 +212,13 @@ class Vehicle(db.Model): tessie_battery_range = db.Column(db.Float) # Last fetched range in km tessie_last_updated = db.Column(db.DateTime) # When Tessie data was last fetched + # Annual mileage tracking + annual_mileage_limit = db.Column(db.Float, nullable=True) + annual_mileage_start_date = db.Column(db.Date, nullable=True) + + # Sharing — if True, all users on this instance can view and log against this vehicle + is_shared = db.Column(db.Boolean, default=False, nullable=False) + # Relationships fuel_logs = db.relationship('FuelLog', backref='vehicle', lazy='dynamic', cascade='all, delete-orphan') @@ -355,9 +363,97 @@ def get_cost_per_distance(self): return None def is_electric(self): - """Check if vehicle is electric or plug-in hybrid""" + """Check if vehicle uses any electric propulsion""" return self.fuel_type in ('electric', 'plugin_hybrid', 'hybrid') + def uses_charging(self): + """Check if vehicle can be plugged in for charging (pure EV or plug-in hybrid)""" + return self.fuel_type in ('electric', 'plugin_hybrid') + + def uses_fuel(self): + """Check if vehicle uses liquid fuel (not pure electric)""" + return self.fuel_type != 'electric' + + def get_annual_mileage_stats(self): + """Return mileage tracking stats for the current annual period, or None if not configured.""" + if not self.annual_mileage_limit or not self.annual_mileage_start_date: + return None + + from datetime import date as date_type + + today = date_type.today() + limit = self.annual_mileage_limit + start = self.annual_mileage_start_date + + # Find the most recent anniversary of start that is <= today + period_year = today.year + try: + candidate = start.replace(year=period_year) + except ValueError: + candidate = start.replace(year=period_year, day=28) + if candidate > today: + period_year -= 1 + try: + candidate = start.replace(year=period_year) + except ValueError: + candidate = start.replace(year=period_year, day=28) + period_start = candidate + + try: + period_end = start.replace(year=period_year + 1) + except ValueError: + period_end = start.replace(year=period_year + 1, day=28) + + days_total = (period_end - period_start).days + days_elapsed = max(0, (today - period_start).days) + days_remaining = max(0, (period_end - today).days) + + # Baseline: last odometer reading before this period + baseline_log = (self.fuel_logs + .filter(FuelLog.date < period_start) + .order_by(FuelLog.date.desc(), FuelLog.odometer.desc()) + .first()) + current_log = (self.fuel_logs + .order_by(FuelLog.date.desc(), FuelLog.odometer.desc()) + .first()) + + if not current_log: + driven = 0.0 + elif baseline_log: + driven = max(0.0, current_log.odometer - baseline_log.odometer) + else: + first_log = (self.fuel_logs + .filter(FuelLog.date >= period_start) + .order_by(FuelLog.date.asc(), FuelLog.odometer.asc()) + .first()) + if first_log and current_log.id != first_log.id: + driven = max(0.0, current_log.odometer - first_log.odometer) + else: + driven = 0.0 + + remaining = max(0.0, limit - driven) + progress_pct = min(100.0, round(driven / limit * 100, 1)) if limit > 0 else 0.0 + time_pct = round(days_elapsed / days_total * 100, 1) if days_total > 0 else 0.0 + expected = round(limit / days_total * days_elapsed) if days_total > 0 else 0 + projected = round(driven / days_elapsed * days_total) if days_elapsed > 0 else 0 + + return { + 'limit': limit, + 'period_start': period_start, + 'period_end': period_end, + 'days_total': days_total, + 'days_elapsed': days_elapsed, + 'days_remaining': days_remaining, + 'driven': round(driven), + 'remaining': round(remaining), + 'projected': projected, + 'on_pace': projected <= limit, + 'over_limit': driven >= limit, + 'progress_pct': progress_pct, + 'time_pct': time_pct, + 'expected': expected, + } + def to_dict(self): """Serialize vehicle to dictionary for API""" return { @@ -698,6 +794,7 @@ def get_all_branding(): EXPENSE_CATEGORIES = [ ('maintenance', _l('Maintenance')), ('repairs', _l('Repairs')), + ('inspection', _l('Inspection')), ('insurance', _l('Insurance')), ('tax', _l('Road Tax')), ('registration', _l('Registration')), diff --git a/app/routes/documents.py b/app/routes/documents.py index ee324b4..43932d3 100644 --- a/app/routes/documents.py +++ b/app/routes/documents.py @@ -10,7 +10,7 @@ bp = Blueprint('documents', __name__, url_prefix='/documents') -ALLOWED_EXTENSIONS = {'pdf', 'png', 'jpg', 'jpeg', 'gif', 'webp', 'doc', 'docx'} +ALLOWED_EXTENSIONS = {'pdf', 'png', 'jpg', 'jpeg', 'gif', 'webp', 'doc', 'docx', 'xlsx', 'xls', 'txt', 'csv', 'epub'} def allowed_file(filename): diff --git a/app/routes/vehicles.py b/app/routes/vehicles.py index a4762ed..a3df7c5 100644 --- a/app/routes/vehicles.py +++ b/app/routes/vehicles.py @@ -7,7 +7,7 @@ from flask_babel import gettext as _ from werkzeug.utils import secure_filename from app import db -from app.models import Vehicle, VehicleSpec, VehiclePart, FuelLog, Expense, User, Reminder, VEHICLE_TYPES, FUEL_TYPES, VEHICLE_SPEC_TYPES, REMINDER_TYPES, PART_TYPES, TRACKING_UNITS, ODOMETER_UNITS, AppSettings +from app.models import Vehicle, VehicleSpec, VehiclePart, FuelLog, Expense, User, Reminder, MaintenanceSchedule, VEHICLE_TYPES, FUEL_TYPES, VEHICLE_SPEC_TYPES, REMINDER_TYPES, PART_TYPES, TRACKING_UNITS, ODOMETER_UNITS, AppSettings from app.services.tessie import TessieService bp = Blueprint('vehicles', __name__, url_prefix='/vehicles') @@ -56,7 +56,9 @@ def new(): fuel_type=request.form.get('fuel_type'), secondary_fuel_type=request.form.get('secondary_fuel_type') or None, tank_capacity=float(request.form.get('tank_capacity')) if request.form.get('tank_capacity') else None, - notes=request.form.get('notes') + notes=request.form.get('notes'), + annual_mileage_limit=float(request.form.get('annual_mileage_limit')) if request.form.get('annual_mileage_limit') else None, + annual_mileage_start_date=datetime.strptime(request.form.get('annual_mileage_start_date'), '%Y-%m-%d').date() if request.form.get('annual_mileage_start_date') else None, ) # Handle image upload @@ -137,6 +139,15 @@ def view(vehicle_id): # Get parts for this vehicle parts = vehicle.parts.order_by(VehiclePart.part_type, VehiclePart.name).all() + # Get active maintenance schedules, sorted soonest-due first + from datetime import date as date_type + today = date_type.today() + maintenance_schedules = vehicle.maintenance_schedules.filter_by(is_active=True).all() + maintenance_schedules.sort(key=lambda s: ( + s.next_due_date or date_type(9999, 12, 31), + s.next_due_odometer or float('inf') + )) + # Check if DVLA integration is configured from app.services.dvla import DVLAService dvla_configured = DVLAService.is_configured() @@ -154,8 +165,11 @@ def view(vehicle_id): reminder_types=REMINDER_TYPES, parts=parts, part_types=PART_TYPES, + maintenance_schedules=maintenance_schedules, + today=today, dvla_configured=dvla_configured, - tessie_configured=tessie_configured) + tessie_configured=tessie_configured, + annual_mileage_stats=vehicle.get_annual_mileage_stats()) @bp.route('//edit', methods=['GET', 'POST']) @@ -182,6 +196,11 @@ def edit(vehicle_id): vehicle.secondary_fuel_type = request.form.get('secondary_fuel_type') or None vehicle.tank_capacity = float(request.form.get('tank_capacity')) if request.form.get('tank_capacity') else None vehicle.notes = request.form.get('notes') + vehicle.annual_mileage_limit = float(request.form.get('annual_mileage_limit')) if request.form.get('annual_mileage_limit') else None + vehicle.annual_mileage_start_date = datetime.strptime(request.form.get('annual_mileage_start_date'), '%Y-%m-%d').date() if request.form.get('annual_mileage_start_date') else None + + vehicle.is_active = request.form.get('is_active') == 'on' + vehicle.is_shared = request.form.get('is_shared') == 'on' # Handle Tessie integration fields vehicle.tessie_vin = request.form.get('tessie_vin') or None diff --git a/app/templates/api/docs.html b/app/templates/api/docs.html index f12384e..0345efe 100644 --- a/app/templates/api/docs.html +++ b/app/templates/api/docs.html @@ -34,18 +34,18 @@

Authentication

Option 1: Authorization Header (Recommended)

-
- Authorization: Bearer may_your_api_key_here +
+ Authorization: Bearer may_your_api_key_here

Option 2: X-API-Key Header

-
- X-API-Key: may_your_api_key_here +
+ X-API-Key: may_your_api_key_here

Example Request

-
-
curl -X GET "{{ request.host_url }}api/v1/vehicles" \
+                
+
curl -X GET "{{ request.host_url }}api/v1/vehicles" \
   -H "Authorization: Bearer may_your_api_key_here"
@@ -78,8 +78,8 @@

Vehicles

List all vehicles you have access to (owned and shared)

Response

-
-
{
+                    
+
{
   "vehicles": [
     {
       "id": 1,
@@ -155,8 +155,8 @@ 

Request Body

Example

-
-
curl -X POST "{{ request.host_url }}api/v1/vehicles" \
+                    
+
curl -X POST "{{ request.host_url }}api/v1/vehicles" \
   -H "Authorization: Bearer may_your_api_key" \
   -H "Content-Type: application/json" \
   -d '{
@@ -231,8 +231,8 @@ 

Query Parameters

Response

-
-
{
+                    
+
{
   "fuel_logs": [
     {
       "id": 1,
@@ -294,8 +294,8 @@ 

Request Body

Example

-
-
curl -X POST "{{ request.host_url }}api/v1/vehicles/1/fuel" \
+                    
+
curl -X POST "{{ request.host_url }}api/v1/vehicles/1/fuel" \
   -H "Authorization: Bearer may_your_api_key" \
   -H "Content-Type: application/json" \
   -d '{
@@ -435,8 +435,8 @@ 

Error Handling

Error Response Format

-
-
{
+                
+
{
   "error": "Human-readable error message",
   "code": "error_code"
 }
diff --git a/app/templates/auth/settings.html b/app/templates/auth/settings.html index a657c8e..126234d 100644 --- a/app/templates/auth/settings.html +++ b/app/templates/auth/settings.html @@ -947,8 +947,8 @@

{{ _('Home Assista

{{ _('REST Sensor Configuration') }}

-
-
sensor:
+                                    
+
sensor:
   - platform: rest
     name: "May Vehicle Stats"
     resource: {{ request.url_root.rstrip('/') }}/api/ha/summary
diff --git a/app/templates/base.html b/app/templates/base.html
index e5f7827..35d9ca8 100644
--- a/app/templates/base.html
+++ b/app/templates/base.html
@@ -96,17 +96,7 @@
         Object.entries(primaryPalette).forEach(([key, value]) => {
             rootStyle.setProperty(`--primary-${key}`, value);
         });
-        tailwind.config = {
-            darkMode: 'class',
-            theme: {
-                extend: {
-                    colors: {
-                        primary: primaryPalette
-                    }
-                }
-            }
-        };
-        window.tailwind.config = tailwind.config;
+        window.__primaryPalette = primaryPalette;
     
     
     
+    
     
     
 {% endblock %}
diff --git a/app/templates/vehicles/form.html b/app/templates/vehicles/form.html
index 4ca99bf..f4f5a79 100644
--- a/app/templates/vehicles/form.html
+++ b/app/templates/vehicles/form.html
@@ -191,6 +191,33 @@ 

{{ _('Specifi

+
+

{{ _('Annual Mileage Tracking') }}

+

{{ _('Set a yearly distance limit to track progress — useful for leasing contracts or insurance policies.') }}

+ +
+
+ + +
+
+ + +

{{ _('Tracking resets on this date each year.') }}

+
+
+
+

{{ _('Notes') }}

@@ -211,6 +238,12 @@

{{ _('Status' class="h-4 w-4 rounded border-gray-300 dark:border-gray-600 text-primary-600 focus:ring-primary-500">

+
+ + +
{% endif %} diff --git a/app/templates/vehicles/index.html b/app/templates/vehicles/index.html index b9fccdd..3a8c083 100644 --- a/app/templates/vehicles/index.html +++ b/app/templates/vehicles/index.html @@ -79,9 +79,14 @@

{% if show_archived

{{ vehicle.name }}

- {% if not vehicle.is_active %} - {{ _('Inactive') }} - {% endif %} +
+ {% if not vehicle.is_active %} + {{ _('Inactive') }} + {% endif %} + {% if vehicle.is_shared %} + {{ _('Shared') }} + {% endif %} +

{{ vehicle.make or '' }} {{ vehicle.model or '' }} {% if vehicle.year %}({{ vehicle.year }}){% endif %}

diff --git a/app/templates/vehicles/view.html b/app/templates/vehicles/view.html index f032f85..ab921ed 100644 --- a/app/templates/vehicles/view.html +++ b/app/templates/vehicles/view.html @@ -99,7 +99,7 @@

{{ vehicle.name }}<
- {% if not vehicle.uses_tessie_odometer() %} + {% if vehicle.uses_fuel() and not vehicle.uses_tessie_odometer() %} @@ -108,7 +108,7 @@

{{ vehicle.name }}< {{ _('Add Fuel Log') }} {% endif %} - {% if vehicle.is_electric() %} + {% if vehicle.uses_charging() %} @@ -365,6 +365,83 @@

{{ _('Tessie Statu

{% endif %} + +{% if annual_mileage_stats %} +{% set ms = annual_mileage_stats %} +
+ + +
+ +
+
+ {{ _('Distance') }}: {{ "{:,}".format(ms.driven) }} / {{ "{:,}".format(ms.limit|int) }} {{ vehicle.get_effective_odometer_unit() }} + {{ ms.progress_pct }}% +
+
+ +
+ +
+
+
+ {{ ms.period_start|format_date }} + ▲ {{ ms.time_pct }}% {{ _('of year elapsed') }} + {{ ms.period_end|format_date }} +
+
+ + +
+
+

{{ "{:,}".format(ms.remaining) }}

+

{{ vehicle.get_effective_odometer_unit() }} {{ _('remaining') }}

+
+
+

{{ ms.days_remaining }}

+

{{ _('days remaining') }}

+
+
+

{{ "{:,}".format(ms.projected) }}

+

{{ _('projected year-end') }}

+
+
+

{{ "{:,}".format(ms.expected) }}

+

{{ _('expected at this point') }}

+
+
+
+
+{% endif %} + {% if reminders %}
@@ -475,17 +552,77 @@

{{ _('Notes') }} {% endif %} - +{% if maintenance_schedules %} +
- + -

{{ _('Parts & Consumables') }}

+

{{ _('Upcoming Maintenance') }}

+
+ {{ _('View all') }} +
+
    + {% for schedule in maintenance_schedules[:5] %} + {% set is_overdue = schedule.next_due_date and schedule.next_due_date < today %} + {% set is_due_soon = schedule.next_due_date and not is_overdue and (schedule.next_due_date - today).days <= 30 %} +
  • +
    +
    +
    + {{ schedule.name }} + {% if is_overdue %} + {{ _('Overdue') }} + {% elif is_due_soon %} + {{ _('Due soon') }} + {% endif %} +
    +
    + {% if schedule.next_due_date %} + {{ schedule.next_due_date|format_date }} + {% endif %} + {% if schedule.next_due_odometer %} + {{ schedule.next_due_odometer|int }} {{ vehicle.get_effective_odometer_unit() }} + {% endif %} +
    +
    + {% if schedule.next_due_date and schedule.last_performed_date %} + {% set total_days = (schedule.next_due_date - schedule.last_performed_date).days %} + {% set elapsed_days = (today - schedule.last_performed_date).days %} + {% set pct = [[(elapsed_days / total_days * 100) if total_days > 0 else 100, 0]|max, 100]|min %} +
    +
    +
    +
    +
    + {% endif %} +
    +
  • + {% endfor %} +
+
+{% endif %} + + +
+
+
+
{{ _('View all') }}
+
{% if parts %}
    {% for part in parts[:6] %} @@ -536,8 +673,27 @@

    {{ _('Parts & Cons

{% endif %} +

+ +

{{ _('Fuel Consumption Trend') }}

diff --git a/app/translations/de/LC_MESSAGES/messages.po b/app/translations/de/LC_MESSAGES/messages.po index b289565..893628a 100644 --- a/app/translations/de/LC_MESSAGES/messages.po +++ b/app/translations/de/LC_MESSAGES/messages.po @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: May 1.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-02-27 11:00+0000\n" -"PO-Revision-Date: 2026-02-17 21:00+0000\n" -"Last-Translator: \n" +"PO-Revision-Date: 2026-04-28 12:00+0000\n" +"Last-Translator: Fleischpirat" "Language: de\n" "Language-Team: German\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" @@ -123,7 +123,7 @@ msgstr "Zubehör" #: app/models.py:653 app/models.py:667 app/models.py:693 app/models.py:725 #: app/models.py:735 app/models.py:769 app/models.py:1133 msgid "Other" -msgstr "andere" +msgstr "Andere" #: app/models.py:658 msgid "Car" @@ -175,7 +175,7 @@ msgstr "Kilometer (km)" #: app/models.py:679 msgid "Miles (mi)" -msgstr "Milen (mi)" +msgstr "Meilen (mi)" #: app/models.py:684 msgid "Petrol/Gasoline" @@ -211,16 +211,15 @@ msgstr "Wasserstoff" #: app/models.py:692 msgid "E85/Flex Fuel" -msgstr "" +msgstr "E85/Ethanol-Kraftstoff" #: app/models.py:698 msgid "MOT/Inspection" msgstr "Inspektion" #: app/models.py:699 -#, fuzzy msgid "Service Due" -msgstr "Service-Verlauf" +msgstr "Service fällig" #: app/models.py:700 msgid "Insurance Renewal" @@ -244,7 +243,7 @@ msgstr "Ölwechsel" #: app/models.py:711 msgid "No Repeat" -msgstr "kene Wiederholung" +msgstr "Keine Wiederholung" #: app/models.py:712 app/templates/recurring/form.html:57 msgid "Monthly" @@ -271,7 +270,6 @@ msgid "Personal" msgstr "Privat" #: app/models.py:722 -#, fuzzy msgid "Commute" msgstr "Pendeln" @@ -301,7 +299,7 @@ msgstr "Schnelladung" #: app/models.py:734 msgid "Tesla Supercharger" -msgstr "esla Supercharger" +msgstr "Tesla Supercharger" #: app/models.py:741 app/models.py:1119 msgid "Oil Filter" @@ -337,7 +335,7 @@ msgstr "Kühlflüssigkeit" #: app/models.py:749 msgid "Transmission Service" -msgstr "Überführungskosten" +msgstr "Getriebeservice" #: app/models.py:750 msgid "Timing Belt" @@ -2981,363 +2979,315 @@ msgid "No trips logged for" msgstr "Keine Fahrten erfasst für" #: app/templates/vehicles/form.html:2 app/templates/vehicles/form.html:8 -#, fuzzy msgid "Edit Vehicle" -msgstr "Fahrzeug" +msgstr "Fahrzeug bearbeiten" #: app/templates/vehicles/form.html:2 app/templates/vehicles/form.html:269 #: app/templates/vehicles/index.html:32 app/templates/vehicles/index.html:108 -#, fuzzy msgid "Add Vehicle" -msgstr "Alle Fahrzeuge" +msgstr "Fahrzeug hinzufügen" #: app/templates/vehicles/form.html:7 app/templates/vehicles/view.html:11 -#, fuzzy msgid "Back to vehicles" -msgstr "Alle Fahrzeuge" +msgstr "Zurück zu Fahrzeugen" #: app/templates/vehicles/form.html:8 -#, fuzzy msgid "Add New Vehicle" -msgstr "Alle Fahrzeuge" +msgstr "Neues Fahrzeug hinzufügen" #: app/templates/vehicles/form.html:14 -#, fuzzy msgid "Basic Information" -msgstr "Kontoinformationen" +msgstr "Grundlegende Informationen" #: app/templates/vehicles/form.html:36 msgid "Tracking Unit" -msgstr "" +msgstr "Messgerät" #: app/templates/vehicles/form.html:43 msgid "Use \"Hours\" for tractors, ATVs, boats, etc." -msgstr "" +msgstr "Verwenden Sie Stunden für zum Beispiel Traktoren, ATVs, Boote, usw." #: app/templates/vehicles/form.html:47 -#, fuzzy msgid "Odometer Unit" -msgstr "Kilometerstand" +msgstr "Kilometerstand-Einheit" #: app/templates/vehicles/form.html:50 -#, fuzzy msgid "Use account default" -msgstr "Ihre Kontodetails" +msgstr "Kontostandard verwenden" #: app/templates/vehicles/form.html:55 msgid "" "Override the odometer unit for this vehicle. Useful when you have " "vehicles with different odometer units." msgstr "" +"Kilometerstand-Einheit für dieses Fahrzeug überschreiben. Nützlich, wenn " +"Sie Fahrzeuge mit unterschiedlichen (z.B. Nicht-Landesüblichen) Einheiten haben." #: app/templates/vehicles/form.html:59 -#, fuzzy msgid "Fuel Type" -msgstr "Typ" +msgstr "Kraftstoffart" #: app/templates/vehicles/form.html:69 msgid "Make" -msgstr "" +msgstr "Hersteller" #: app/templates/vehicles/form.html:77 -#, fuzzy msgid "Model" -msgstr "Mehr" +msgstr "Modell" #: app/templates/vehicles/form.html:94 msgid "Tank Capacity (L)" -msgstr "" +msgstr "Tankvolumen (L)" #: app/templates/vehicles/form.html:104 -#, fuzzy msgid "Identification" -msgstr "Benachrichtigungen" +msgstr "Identifikation" #: app/templates/vehicles/form.html:108 -#, fuzzy msgid "Registration / License Plate" -msgstr "Registrierungseinstellungen aktualisiert" +msgstr "Kennzeichen" #: app/templates/vehicles/form.html:116 app/templates/vehicles/view.html:328 msgid "VIN" msgstr "FIN" #: app/templates/vehicles/form.html:126 -#, fuzzy msgid "Photo" -msgstr "Postleitzahl" +msgstr "Foto" #: app/templates/vehicles/form.html:129 -#, fuzzy msgid "Vehicle Image" -msgstr "Fahrzeug" +msgstr "Fahrzeugbild" #: app/templates/vehicles/form.html:143 app/templates/vehicles/view.html:447 -#, fuzzy msgid "Specifications" -msgstr "Benachrichtigungen" +msgstr "Spezifikationen" #: app/templates/vehicles/form.html:144 msgid "Add technical details like tire sizes, oil type, wiper sizes, etc." -msgstr "" +msgstr "Technische Details wie Reifengrößen, Öltyp, Wischblattgrößen, usw. hinzufügen." #: app/templates/vehicles/form.html:178 -#, fuzzy msgid "Add Specification" -msgstr "Tankstelle hinzufügen" +msgstr "Spezifikation hinzufügen" #: app/templates/vehicles/form.html:186 -#, fuzzy msgid "General Notes" -msgstr "Optionale Notizen" +msgstr "Allgemeine Notizen" #: app/templates/vehicles/form.html:195 -#, fuzzy msgid "Status" -msgstr "Steuerstatus" +msgstr "Status" #: app/templates/vehicles/form.html:200 msgid "Active (show in lists and calculations)" -msgstr "" +msgstr "Aktiv (in Listen und Berechnungen anzeigen)" #: app/templates/vehicles/form.html:216 msgid "" "Link this vehicle to Tessie for automatic odometer and battery tracking. " "When enabled, manual odometer entry will be disabled." msgstr "" +"Dieses Fahrzeug mit Tessie verknüpfen für automatische Kilometerstand- " +"und Batterieverfolgung. Bei Aktivierung wird die manuelle Eingabe deaktiviert." #: app/templates/vehicles/form.html:220 -#, fuzzy msgid "Tessie VIN" -msgstr "Tessie" +msgstr "Tessie FIN" #: app/templates/vehicles/form.html:232 -#, fuzzy msgid "Lookup" -msgstr "Abmelden" +msgstr "Suchen" #: app/templates/vehicles/form.html:243 msgid "Enable Tessie tracking (disables manual odometer entry)" -msgstr "" +msgstr "Tessie-Tracking aktivieren (deaktiviert manuelle Kilometerstandeingabe)" #: app/templates/vehicles/form.html:253 -#, fuzzy msgid "Tessie tracking is active for this vehicle" -msgstr "Noch keine Tessie-Daten für dieses Fahrzeug verfügbar." +msgstr "Tessie-Tracking ist für dieses Fahrzeug aktiv" #: app/templates/vehicles/index.html:7 -#, fuzzy msgid "Archived Vehicles" -msgstr "Alle Fahrzeuge" +msgstr "Archivierte Fahrzeuge" #: app/templates/vehicles/index.html:8 msgid "Manage your cars, vans, motorbikes, and scooters" -msgstr "" +msgstr "Verwalten Sie Ihre Autos, Vans, Motorräder und Roller" #: app/templates/vehicles/index.html:18 -#, fuzzy msgid "Active Vehicles" -msgstr "Alle Fahrzeuge" +msgstr "Aktive Fahrzeuge" #: app/templates/vehicles/index.html:23 -#, fuzzy msgid "Archived" -msgstr "Aktivieren" +msgstr "Archiviert" #: app/templates/vehicles/index.html:83 -#, fuzzy msgid "Inactive" -msgstr "Aktivieren" +msgstr "Inaktiv" #: app/templates/vehicles/index.html:100 -#, fuzzy msgid "No vehicles" -msgstr "Fahrzeuge" +msgstr "Keine Fahrzeuge" #: app/templates/vehicles/index.html:101 msgid "Get started by adding your first vehicle." -msgstr "" +msgstr "Fügen Sie Ihr erstes Fahrzeug hinzu, um zu beginnen." #: app/templates/vehicles/part_form.html:2 #: app/templates/vehicles/part_form.html:8 -#, fuzzy msgid "Edit Part" -msgstr "Fahrt bearbeiten" +msgstr "Teil bearbeiten" #: app/templates/vehicles/part_form.html:2 #: app/templates/vehicles/part_form.html:8 #: app/templates/vehicles/part_form.html:102 #: app/templates/vehicles/parts.html:19 app/templates/vehicles/view.html:530 -#, fuzzy msgid "Add Part" -msgstr "Teile" +msgstr "Teil hinzufügen" #: app/templates/vehicles/part_form.html:7 -#, fuzzy msgid "Back to parts" -msgstr "Zurück zu Fahrten" +msgstr "Zurück zu Teilen" #: app/templates/vehicles/part_form.html:15 -#, fuzzy msgid "Part Details" -msgstr "Kontodetails" +msgstr "Teildetails" #: app/templates/vehicles/part_form.html:19 -#, fuzzy msgid "Part Type" -msgstr "Ladetyp" +msgstr "Teiltyp" #: app/templates/vehicles/part_form.html:37 -#, fuzzy msgid "Specification" -msgstr "Benachrichtigungen" +msgstr "Spezifikation" #: app/templates/vehicles/part_form.html:42 msgid "Oil viscosity, filter model, tire size, etc." -msgstr "" +msgstr "Ölviskosität, Filtermodell, Reifengröße, usw." #: app/templates/vehicles/part_form.html:46 msgid "Quantity" -msgstr "" +msgstr "Menge" #: app/templates/vehicles/part_form.html:54 -#, fuzzy msgid "Unit" -msgstr "Betrag" +msgstr "Einheit" #: app/templates/vehicles/part_form.html:57 -#, fuzzy msgid "Select unit..." -msgstr "Typ auswählen..." +msgstr "Einheit auswählen..." #: app/templates/vehicles/part_form.html:58 msgid "Liters (L)" -msgstr "" +msgstr "Liter (L)" #: app/templates/vehicles/part_form.html:59 msgid "Milliliters (ml)" -msgstr "" +msgstr "Milliliter (ml)" #: app/templates/vehicles/part_form.html:60 msgid "Gallons (gal)" -msgstr "" +msgstr "Gallonen (gal)" #: app/templates/vehicles/part_form.html:61 msgid "Quarts (qt)" -msgstr "" +msgstr "Quart (qt)" #: app/templates/vehicles/part_form.html:62 -#, fuzzy msgid "Units" -msgstr "Notizen" +msgstr "Einheiten" #: app/templates/vehicles/part_form.html:63 -#, fuzzy msgid "Pieces" -msgstr "API-Zugriff" +msgstr "Stück" #: app/templates/vehicles/part_form.html:64 -#, fuzzy msgid "Set" -msgstr "TLS verwenden" +msgstr "Satz" #: app/templates/vehicles/part_form.html:65 -#, fuzzy msgid "Pair" -msgstr "Teile" +msgstr "Paar" #: app/templates/vehicles/part_form.html:70 msgid "Part Number" -msgstr "" +msgstr "Teilenummer" #: app/templates/vehicles/part_form.html:78 msgid "Supplier URL" -msgstr "" +msgstr "Lieferanten-URL" #: app/templates/vehicles/part_form.html:83 msgid "Link to purchase this part" -msgstr "" +msgstr "Link zum Kauf dieses Teils" #: app/templates/vehicles/parts.html:2 app/templates/vehicles/parts.html:11 #: app/templates/vehicles/view.html:480 msgid "Parts & Consumables" -msgstr "" +msgstr "Teile & Verbrauchsmaterialien" #: app/templates/vehicles/parts.html:47 msgid "Qty:" -msgstr "" +msgstr "Menge:" #: app/templates/vehicles/parts.html:94 -#, fuzzy msgid "No parts added" -msgstr "Zugeführte Energie" +msgstr "Keine Teile hinzugefügt" #: app/templates/vehicles/parts.html:95 msgid "Track the parts and consumables needed for servicing this vehicle." -msgstr "" - -#: app/templates/vehicles/parts.html:102 -#, fuzzy -msgid "Add First Part" -msgstr "Tankstelle hinzufügen" +msgstr "Erfassen Sie die Teile und Verbrauchsmaterialien, die für die Wartung dieses Fahrzeugs benötigt werden." #: app/templates/vehicles/share.html:2 app/templates/vehicles/share.html:20 #: app/templates/vehicles/view.html:44 -#, fuzzy msgid "Share" -msgstr "Speichern" +msgstr "Teilen" #: app/templates/vehicles/share.html:8 -#, fuzzy msgid "Share Vehicle" -msgstr "Alle Fahrzeuge" +msgstr "Fahrzeug teilen" #: app/templates/vehicles/share.html:9 msgid "Allow other users to view and add logs to this vehicle" -msgstr "" +msgstr "Anderen Benutzern erlauben, dieses Fahrzeug einzusehen und Einträge hinzuzufügen." #: app/templates/vehicles/share.html:13 -#, fuzzy msgid "Add User" -msgstr "Benutzer erstellen" +msgstr "Benutzer hinzufügen" #: app/templates/vehicles/share.html:16 -#, fuzzy msgid "Enter username" -msgstr "SMTP-Benutzername" +msgstr "Benutzername eingeben" #: app/templates/vehicles/share.html:27 msgid "Shared With" -msgstr "" +msgstr "Geteilt mit" #: app/templates/vehicles/share.html:39 -#, fuzzy msgid "Remove" -msgstr "Logo entfernen" +msgstr "Entfernen" #: app/templates/vehicles/share.html:45 msgid "This vehicle is not shared with anyone yet." -msgstr "" +msgstr "Dieses Fahrzeug wurde noch mit niemandem geteilt." #: app/templates/vehicles/view.html:32 -#, fuzzy msgid "Download PDF Report" -msgstr "ZIP herunterladen" +msgstr "PDF-Bericht herunterladen" #: app/templates/vehicles/view.html:56 app/templates/vehicles/view.html:60 -#, fuzzy msgid "Not set" -msgstr "Notizen" +msgstr "Nicht gesetzt" #: app/templates/vehicles/view.html:63 -#, fuzzy msgid "Last Odometer" msgstr "Letzter Kilometerstand" #: app/templates/vehicles/view.html:89 -#, fuzzy msgid "Avg. Consumption" -msgstr "Kraftstoffverbrauch" +msgstr "Durchschnittsverbrauch" #: app/templates/vehicles/view.html:103 msgid "Add Fuel Log" @@ -3428,132 +3378,115 @@ msgid "Fetch from Tessie" msgstr "Von Tessie abrufen" #: app/templates/vehicles/view.html:358 -msgid "" -"This vehicle is archived. It won't appear in regular lists or " -"calculations." -msgstr "" +msgid "This vehicle is archived. It won't appear in regular lists or calculations." +msgstr "Dieses Fahrzeug ist archiviert. Es wird nicht in regulären Listen oder Berechnungen angezeigt." #: app/templates/vehicles/view.html:371 -#, fuzzy msgid "Upcoming Reminders" msgstr "Anstehende Erinnerungen" #: app/templates/vehicles/view.html:383 -#, fuzzy msgid "Mark as completed" msgstr "Als erledigt markieren" #: app/templates/vehicles/view.html:410 #, python-format msgid "%(days)s days overdue" -msgstr "" +msgstr "%(days)s Tage überfällig" #: app/templates/vehicles/view.html:412 -#, fuzzy msgid "Due today" -msgstr "Jetzt fällig" +msgstr "Heute fällig" #: app/templates/vehicles/view.html:414 -#, fuzzy msgid "Due tomorrow" -msgstr "Jetzt fällig" +msgstr "Morgen fällig" #: app/templates/vehicles/view.html:416 #, python-format msgid "In %(days)s days" -msgstr "" +msgstr "In %(days)s Tagen" #: app/templates/vehicles/view.html:524 msgid "No parts added yet." -msgstr "" +msgstr "Noch keine Teile hinzugefügt." #: app/templates/vehicles/view.html:538 -#, fuzzy msgid "Fuel Consumption Trend" -msgstr "Kraftstoffverbrauch" +msgstr "Kraftstoffverbrauch-Trend" #: app/templates/vehicles/view.html:561 -#, fuzzy msgid "(Full)" -msgstr "Kraftstoff" +msgstr "(Voll)" #: app/templates/vehicles/view.html:578 -#, fuzzy msgid "Delete this fuel log?" -msgstr "Diese Tankstelle löschen?" +msgstr "Dieses Tankprotokoll löschen?" #: app/templates/vehicles/view.html:593 -#, fuzzy msgid "No fuel logs yet." -msgstr "Tankprotokoll" +msgstr "Noch keine Tankprotokolle vorhanden." #: app/templates/vehicles/view.html:622 -#, fuzzy msgid "Delete this expense?" -msgstr "Diese wiederkehrende Ausgabe löschen?" +msgstr "Diese Ausgabe löschen?" #: app/templates/vehicles/view.html:637 -#, fuzzy msgid "No expenses yet." -msgstr "Sonstige Ausgaben" +msgstr "Noch keine Ausgaben vorhanden." #: app/templates/vehicles/view.html:653 -#, fuzzy msgid "Archive this vehicle" -msgstr "Ungültiges Fahrzeug" +msgstr "Dieses Fahrzeug archivieren" #: app/templates/vehicles/view.html:654 msgid "" "Archived vehicles won't appear in regular lists or calculations. You can " "restore them later." msgstr "" +"Archivierte Fahrzeuge werden nicht in regulären Listen oder Berechnungen " +"angezeigt. Sie können diese später wiederherstellen." #: app/templates/vehicles/view.html:656 -#, fuzzy msgid "Restore this vehicle" -msgstr "Ihre Fahrzeuge" +msgstr "Dieses Fahrzeug wiederherstellen" #: app/templates/vehicles/view.html:657 msgid "" "Restore this vehicle to make it active again and include it in " "calculations." msgstr "" +"Stellen Sie dieses Fahrzeug wieder her, um es erneut zu aktivieren und " +"in Berechnungen einzubeziehen." #: app/templates/vehicles/view.html:665 -#, fuzzy msgid "Archive Vehicle" -msgstr "Ungültiges Fahrzeug" +msgstr "Fahrzeug archivieren" #: app/templates/vehicles/view.html:673 -#, fuzzy msgid "Restore Vehicle" -msgstr "Ihre Fahrzeuge" +msgstr "Fahrzeug wiederherstellen" #: app/templates/vehicles/view.html:683 -#, fuzzy msgid "Delete this vehicle" -msgstr "Dieses Dokument löschen?" +msgstr "Dieses Fahrzeug löschen" #: app/templates/vehicles/view.html:684 msgid "" "Once you delete a vehicle, there is no going back. This will also delete " "all associated fuel logs and expenses." msgstr "" +"Das Löschen eines Fahrzeugs kann nicht rückgängig gemacht werden. Alle " +"zugehörigen Tankprotokolle und Ausgaben werden ebenfalls gelöscht." #: app/templates/vehicles/view.html:687 msgid "" "Are you sure you want to delete this vehicle? This action cannot be " "undone." msgstr "" +"Sind Sie sicher, dass Sie dieses Fahrzeug löschen möchten? Diese Aktion " +"kann nicht rückgängig gemacht werden." #: app/templates/vehicles/view.html:691 -#, fuzzy msgid "Delete Vehicle" -msgstr "Alle Fahrzeuge" - -#~ msgid "Make" -#~ msgstr "Marke" - -#~ msgid "Average Consumption" -#~ msgstr "Durchschnittsverbrauch" - +msgstr "Fahrzeug löschen" diff --git a/app/translations/it/LC_MESSAGES/messages.po b/app/translations/it/LC_MESSAGES/messages.po index a9dae3b..a442e83 100644 --- a/app/translations/it/LC_MESSAGES/messages.po +++ b/app/translations/it/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: May 1.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-02-27 11:00+0000\n" -"PO-Revision-Date: 2025-02-06 15:49+0100\n" +"PO-Revision-Date: 2026-04-18 15:49+0100\n" "Last-Translator: Albano Battistella \n" "Language: it\n" "Language-Team: it\n" @@ -19,75 +19,68 @@ msgstr "" #: app/models.py:624 msgid "Front Tire Size" -msgstr "" +msgstr "Dimensione Pneumatico Anteriore" #: app/models.py:625 -#, fuzzy msgid "Rear Tire Size" -msgstr "Crea Utente" +msgstr "Dimensione Pneumatico Posteriore" #: app/models.py:626 msgid "Wheel Size" -msgstr "" +msgstr "Dimensione Ruota" #: app/models.py:627 -#, fuzzy msgid "Engine Oil Type" -msgstr "Tipo di file non valido" +msgstr "Tipo Olio Motore" #: app/models.py:628 msgid "Oil Capacity" -msgstr "" +msgstr "Capacità Olio" #: app/models.py:629 -#, fuzzy msgid "Coolant Type" -msgstr "Tipo di Caricatore" +msgstr "Tipo di Liquido di Raffreddamento" #: app/models.py:630 msgid "Front Wiper Size" -msgstr "" +msgstr "Dimensione Tergicristallo Anteriore" #: app/models.py:631 msgid "Rear Wiper Size" -msgstr "" +msgstr "Dimensione Tergicristallo Posteriore" #: app/models.py:632 -#, fuzzy msgid "Battery Type" -msgstr "Livello Batteria" +msgstr "Tipo Batteria" #: app/models.py:633 -#, fuzzy msgid "Spark Plug Type" -msgstr "Tipo di Caricatore" +msgstr "Tipo Candela" #: app/models.py:634 -#, fuzzy msgid "Air Filter Part #" -msgstr "Aggiungi Stazione" +msgstr "Filtro Aria Codice Ricambio" #: app/models.py:635 -#, fuzzy msgid "Cabin Filter Part #" -msgstr "Aggiungi Stazione" +msgstr "Filtro Abitacolo Codice Ricambio" #: app/models.py:636 msgid "Front Brake Pads" -msgstr "" +msgstr "Pastiglie Freno Anteriori" #: app/models.py:637 msgid "Rear Brake Pads" -msgstr "" +msgstr "Pastiglie Freno Posteriori" #: app/models.py:638 app/models.py:1127 msgid "Transmission Fluid" -msgstr "" +msgstr "Olio Cambio" #: app/models.py:639 app/models.py:706 app/models.py:757 #: app/templates/auth/settings.html:176 msgid "Custom" -msgstr "Personalizzata" +msgstr "Personalizzato" #: app/models.py:644 app/templates/auth/settings.html:408 #: app/templates/auth/settings.html:459 app/templates/base.html:301 @@ -96,393 +89,349 @@ msgid "Maintenance" msgstr "Manutenzione" #: app/models.py:645 -#, fuzzy msgid "Repairs" -msgstr "Ricambi" +msgstr "Riparazioni" #: app/models.py:646 -#, fuzzy msgid "Insurance" -msgstr "Distanza" +msgstr "Assicurazione" #: app/models.py:647 app/models.py:701 msgid "Road Tax" -msgstr "" +msgstr "Bollo Auto" #: app/models.py:648 app/templates/vehicles/view.html:59 -#, fuzzy msgid "Registration" -msgstr "Consenti Registrazione" +msgstr "Registrazione" #: app/models.py:649 -#, fuzzy msgid "Parking" -msgstr "Ricarica" +msgstr "Parcheggio" #: app/models.py:650 -#, fuzzy msgid "Tolls" -msgstr "Totale" +msgstr "Pedaggi" #: app/models.py:651 -#, fuzzy msgid "Cleaning" -msgstr "Ricarica" +msgstr "Pulizia" #: app/models.py:652 -#, fuzzy msgid "Accessories" -msgstr "Accesso negato" +msgstr "Accessori" #: app/models.py:653 app/models.py:667 app/models.py:693 app/models.py:725 #: app/models.py:735 app/models.py:769 app/models.py:1133 -#, fuzzy msgid "Other" -msgstr "Contachilometri" +msgstr "Altro" #: app/models.py:658 -#, fuzzy msgid "Car" -msgstr "Cancella" +msgstr "Auto" #: app/models.py:659 -#, fuzzy msgid "Van" -msgstr "VIN" +msgstr "Furgone" #: app/models.py:660 -#, fuzzy msgid "Motorbike" -msgstr "Altro" +msgstr "Moto" #: app/models.py:661 -#, fuzzy msgid "Scooter" -msgstr "Contachilometri" +msgstr "Scooter" #: app/models.py:662 msgid "Truck" -msgstr "" +msgstr "Camion" #: app/models.py:663 msgid "SUV" -msgstr "" +msgstr "SUV" #: app/models.py:664 -#, fuzzy msgid "Tractor" -msgstr "Amministratore" +msgstr "Trattore" #: app/models.py:665 msgid "ATV/UTV" -msgstr "" +msgstr "ATV/UTV" #: app/models.py:666 -#, fuzzy msgid "Boat" -msgstr "Informazioni" +msgstr "Barca" #: app/models.py:672 msgid "Mileage (km/mi)" -msgstr "" +msgstr "Chilometraggio (km/mi)" #: app/models.py:673 -#, fuzzy msgid "Hours" -msgstr "Utenti" +msgstr "Ore" #: app/models.py:678 msgid "Kilometres (km)" -msgstr "" +msgstr "Chilometri (km)" #: app/models.py:679 -#, fuzzy msgid "Miles (mi)" -msgstr "Email (SMTP)" +msgstr "Miglia (mi)" #: app/models.py:684 msgid "Petrol/Gasoline" -msgstr "" +msgstr "Benzina" #: app/models.py:685 msgid "Diesel" -msgstr "" +msgstr "Diesel" #: app/models.py:686 msgid "Electric" -msgstr "" +msgstr "Elettrico" #: app/models.py:687 msgid "Hybrid" -msgstr "" +msgstr "Ibrido" #: app/models.py:688 msgid "Plug-in Hybrid" -msgstr "" +msgstr "Ibrido Plug-in" #: app/models.py:689 msgid "LPG" -msgstr "" +msgstr "GPL" #: app/models.py:690 msgid "CNG" -msgstr "" +msgstr "Metano" #: app/models.py:691 msgid "Hydrogen" -msgstr "" +msgstr "Idrogeno" #: app/models.py:692 msgid "E85/Flex Fuel" -msgstr "" +msgstr "E85/Flex Fuel" #: app/models.py:698 msgid "MOT/Inspection" -msgstr "" +msgstr "Revisione/Ispezione" #: app/models.py:699 -#, fuzzy msgid "Service Due" -msgstr "Cronologia Servizi" +msgstr "Servizio in Scadenza" #: app/models.py:700 msgid "Insurance Renewal" -msgstr "" +msgstr "Rinnovo Assicurazione" #: app/models.py:702 -#, fuzzy msgid "Registration Renewal" -msgstr "Consenti Registrazione" +msgstr "Rinnovo Registrazione" #: app/models.py:703 -#, fuzzy msgid "Warranty Expiry" -msgstr "Scadenza MOT" +msgstr "Scadenza Garanzia" #: app/models.py:704 -#, fuzzy msgid "Tire Change" -msgstr "Salva Modifiche" +msgstr "Cambio Pneumatici" #: app/models.py:705 app/models.py:740 -#, fuzzy msgid "Oil Change" -msgstr "es., Cambio Olio" +msgstr "Cambio Olio" #: app/models.py:711 msgid "No Repeat" -msgstr "" +msgstr "Nessuna Ripetizione" #: app/models.py:712 app/templates/recurring/form.html:57 msgid "Monthly" msgstr "Mensile" #: app/models.py:713 -#, fuzzy msgid "Quarterly (3 months)" -msgstr "Trimestrale (ogni 3 mesi)" +msgstr "Trimestrale (3 mesi)" #: app/models.py:714 -#, fuzzy msgid "Every 6 months" -msgstr "Ogni (mesi)" +msgstr "Ogni 6 mesi" #: app/models.py:715 app/templates/recurring/form.html:60 msgid "Yearly" msgstr "Annuale" #: app/models.py:720 -#, fuzzy msgid "Business" -msgstr "Usa SSL" +msgstr "Lavoro" #: app/models.py:721 -#, fuzzy msgid "Personal" -msgstr "Versione" +msgstr "Personale" #: app/models.py:722 -#, fuzzy msgid "Commute" -msgstr "Completa" +msgstr "Pendolarismo" #: app/models.py:723 msgid "Medical" -msgstr "" +msgstr "Medico" #: app/models.py:724 -#, fuzzy msgid "Charity" -msgstr "Città" +msgstr "Beneficenza" #: app/models.py:730 -#, fuzzy msgid "Home Charging" -msgstr "Ricarica EV" +msgstr "Ricarica Domestica" #: app/models.py:731 msgid "Level 1" -msgstr "" +msgstr "Livello 1" #: app/models.py:732 msgid "Level 2" -msgstr "" +msgstr "Livello 2" #: app/models.py:733 -#, fuzzy msgid "DC Fast Charge" -msgstr "Registra la Tua Prima Ricarica" +msgstr "Ricarica Rapida AC" #: app/models.py:734 msgid "Tesla Supercharger" -msgstr "" +msgstr "Supercharger Tesla" #: app/models.py:741 app/models.py:1119 -#, fuzzy msgid "Oil Filter" -msgstr "Filtra" +msgstr "Filtro Olio" #: app/models.py:742 app/models.py:1120 -#, fuzzy msgid "Air Filter" -msgstr "Filtra" +msgstr "Filtro Aria" #: app/models.py:743 msgid "Cabin/Pollen Filter" -msgstr "" +msgstr "Filtro Abitacolo/Antipolline" #: app/models.py:744 app/models.py:1121 -#, fuzzy msgid "Fuel Filter" -msgstr "Filtra" +msgstr "Filtro Carburante" #: app/models.py:745 msgid "Spark Plugs" -msgstr "" +msgstr "Candele" #: app/models.py:746 -#, fuzzy msgid "Brake Pads" -msgstr "Torna ai viaggi" +msgstr "Pastiglie Freno" #: app/models.py:747 app/models.py:1125 msgid "Brake Fluid" -msgstr "" +msgstr "Liquido Freni" #: app/models.py:748 msgid "Coolant Flush" -msgstr "" +msgstr "Lavaggio Liquido di Raffreddamento" #: app/models.py:749 -#, fuzzy msgid "Transmission Service" -msgstr "Servizi di Notifica" +msgstr "Manutenzione Cambio" #: app/models.py:750 msgid "Timing Belt" -msgstr "" +msgstr "Cinghia di Distribuzione" #: app/models.py:751 msgid "Serpentine Belt" -msgstr "" +msgstr "Cinghia Servizi" #: app/models.py:752 -#, fuzzy msgid "Tire Rotation" -msgstr "Integrazione Tessie" +msgstr "Rotazione Pneumatici" #: app/models.py:753 msgid "Wheel Alignment" -msgstr "" +msgstr "Convergenza Ruote" #: app/models.py:754 msgid "Battery Check/Replace" -msgstr "" +msgstr "Controllo/Sostituzione Batteria" #: app/models.py:755 msgid "Wiper Blades" -msgstr "" +msgstr "Tergicristalli" #: app/models.py:756 msgid "Full Service" -msgstr "" +msgstr "Tagliando Completo" #: app/models.py:762 -#, fuzzy msgid "Insurance Policy" -msgstr "es., Polizza Assicurativa 2024" +msgstr "Polizza Assicurativa" #: app/models.py:763 -#, fuzzy msgid "Registration/V5C" -msgstr "Consenti Registrazione" +msgstr "Registrazione/V5C" #: app/models.py:764 msgid "MOT Certificate" -msgstr "" +msgstr "Certificato Revisione" #: app/models.py:765 -#, fuzzy msgid "Service Record" -msgstr "Intervallo di Servizio" +msgstr "Storico Manutenzione" #: app/models.py:766 msgid "Purchase Invoice" -msgstr "" +msgstr "Fattura d'Acquisto" #: app/models.py:767 -#, fuzzy msgid "Warranty Document" -msgstr "Nessun documento" +msgstr "Documento di Garanzia" #: app/models.py:768 msgid "Owner's Manual" -msgstr "" +msgstr "Manuale d'Uso" #: app/models.py:1118 msgid "Engine Oil" -msgstr "" +msgstr "Olio Motore" #: app/models.py:1122 -#, fuzzy msgid "Cabin Filter" -msgstr "Filtra" +msgstr "Filtro Abitacolo" #: app/models.py:1123 -#, fuzzy msgid "Spark Plug" -msgstr "Pagina Iniziale" +msgstr "Candela" #: app/models.py:1124 msgid "Brake Pad" -msgstr "" +msgstr "Pastiglia Freno" #: app/models.py:1126 msgid "Coolant" -msgstr "" +msgstr "Liquido di Raffreddamento" #: app/models.py:1128 -#, fuzzy msgid "Battery" -msgstr "Livello Batteria" +msgstr "Batteria" #: app/models.py:1129 -#, fuzzy msgid "Tire" -msgstr "Titolo" +msgstr "Pneumatico" #: app/models.py:1130 -#, fuzzy msgid "Belt" -msgstr "Elimina" +msgstr "Cinghia" #: app/models.py:1131 msgid "Wiper Blade" -msgstr "" +msgstr "Tergicristallo" #: app/models.py:1132 msgid "Light Bulb" -msgstr "" +msgstr "Lampadina" #: app/routes/api.py:2141 app/routes/api.py:2354 app/routes/api.py:2527 msgid "No file uploaded" @@ -1040,7 +989,7 @@ msgstr "Ricarica EV" #: app/templates/base.html:325 app/templates/base.html:387 #: app/templates/fuel/quick.html:7 msgid "Quick Fuel Entry" -msgstr "Rifornimento Rapido" +msgstr "Inserimento Rapido Carburante" #: app/templates/base.html:329 app/templates/timeline/index.html:102 #: app/templates/vehicles/view.html:55 @@ -2213,7 +2162,7 @@ msgstr "Posizione" #: app/templates/charging/form.html:67 msgid "e.g., Home, Walmart, ChargePoint Station" -msgstr "es., Casa, Centro Commerciale, Stazione ChargePoint" +msgstr "es., Casa, Walmart, Stazione ChargePoint" #: app/templates/charging/form.html:72 msgid "Network" @@ -2395,7 +2344,7 @@ msgstr "Eliminare questo documento?" #: app/templates/documents/index.html:8 msgid "Vehicle documents, insurance, MOT certificates" -msgstr "Documenti del veicolo, assicurazione, certificati MOT" +msgstr "Documenti del veicolo, assicurazione, certificati revisione" #: app/templates/documents/index.html:74 msgid "Expires" @@ -2413,7 +2362,7 @@ msgstr "Nessun documento" msgid "Store your vehicle documents, insurance policies, and MOT certificates." msgstr "" "Archivia i documenti del veicolo, le polizze assicurative e i certificati" -" MOT." +" revisione." #: app/templates/documents/view.html:30 app/templates/maintenance/index.html:83 #: app/templates/stations/index.html:65 app/templates/vehicles/view.html:40 @@ -3022,363 +2971,329 @@ msgid "No trips logged for" msgstr "Nessun viaggio registrato per" #: app/templates/vehicles/form.html:2 app/templates/vehicles/form.html:8 -#, fuzzy msgid "Edit Vehicle" -msgstr "Veicolo" +msgstr "Modifica Veicolo" #: app/templates/vehicles/form.html:2 app/templates/vehicles/form.html:269 #: app/templates/vehicles/index.html:32 app/templates/vehicles/index.html:108 -#, fuzzy msgid "Add Vehicle" -msgstr "Tutti i Veicoli" +msgstr "Aggiungi Veicolo" #: app/templates/vehicles/form.html:7 app/templates/vehicles/view.html:11 -#, fuzzy msgid "Back to vehicles" -msgstr "Tutti i Veicoli" +msgstr "Torna ai Veicoli" #: app/templates/vehicles/form.html:8 -#, fuzzy msgid "Add New Vehicle" -msgstr "Tutti i Veicoli" +msgstr "Aggiungi Nuovo Veicolo" #: app/templates/vehicles/form.html:14 -#, fuzzy msgid "Basic Information" -msgstr "Informazioni Account" +msgstr "Informazioni di Base" #: app/templates/vehicles/form.html:36 msgid "Tracking Unit" -msgstr "" +msgstr "Unità di Monitoraggio" #: app/templates/vehicles/form.html:43 msgid "Use \"Hours\" for tractors, ATVs, boats, etc." -msgstr "" +msgstr "Usa \"Ore\" per trattori, ATV, barche, ecc." #: app/templates/vehicles/form.html:47 -#, fuzzy msgid "Odometer Unit" -msgstr "Contachilometri" +msgstr "Unità Contachilometri" #: app/templates/vehicles/form.html:50 -#, fuzzy msgid "Use account default" -msgstr "I dettagli del tuo account" +msgstr "Usa impostazioni predefinite account" #: app/templates/vehicles/form.html:55 msgid "" "Override the odometer unit for this vehicle. Useful when you have " "vehicles with different odometer units." msgstr "" +"Ignora l'unità del contachilometri per questo veicolo. Utile quando hai " +"veicoli con unità di contachilometri diverse." #: app/templates/vehicles/form.html:59 -#, fuzzy msgid "Fuel Type" -msgstr "Tipo" +msgstr "Tipo di Carburante" #: app/templates/vehicles/form.html:69 msgid "Make" -msgstr "" +msgstr "Marca" #: app/templates/vehicles/form.html:77 -#, fuzzy msgid "Model" -msgstr "Altro" +msgstr "Modello" #: app/templates/vehicles/form.html:94 msgid "Tank Capacity (L)" -msgstr "" +msgstr "Capacità Serbatoio (L)" #: app/templates/vehicles/form.html:104 -#, fuzzy msgid "Identification" -msgstr "Notifiche" +msgstr "Identificazione" #: app/templates/vehicles/form.html:108 -#, fuzzy msgid "Registration / License Plate" -msgstr "Impostazioni di registrazione aggiornate" +msgstr "Registrazione / Targa" #: app/templates/vehicles/form.html:116 app/templates/vehicles/view.html:328 msgid "VIN" msgstr "VIN" #: app/templates/vehicles/form.html:126 -#, fuzzy msgid "Photo" -msgstr "CAP" +msgstr "Foto" #: app/templates/vehicles/form.html:129 -#, fuzzy msgid "Vehicle Image" -msgstr "Veicolo" +msgstr "Immagine Veicolo" #: app/templates/vehicles/form.html:143 app/templates/vehicles/view.html:447 -#, fuzzy msgid "Specifications" -msgstr "Notifiche" +msgstr "Specifiche" #: app/templates/vehicles/form.html:144 msgid "Add technical details like tire sizes, oil type, wiper sizes, etc." msgstr "" +"Aggiungi dettagli tecnici come dimensioni pneumatici, tipo olio, dimensioni " +"tergicristalli, ecc." #: app/templates/vehicles/form.html:178 -#, fuzzy msgid "Add Specification" -msgstr "Aggiungi Stazione" +msgstr "Aggiungi Specifica" #: app/templates/vehicles/form.html:186 -#, fuzzy msgid "General Notes" -msgstr "Note opzionali" +msgstr "Note Generali" #: app/templates/vehicles/form.html:195 -#, fuzzy msgid "Status" -msgstr "Stato Bollo" +msgstr "Stato" #: app/templates/vehicles/form.html:200 msgid "Active (show in lists and calculations)" -msgstr "" +msgstr "Attivo (mostra in elenchi e calcoli)" #: app/templates/vehicles/form.html:216 msgid "" "Link this vehicle to Tessie for automatic odometer and battery tracking. " "When enabled, manual odometer entry will be disabled." msgstr "" +"Collega questo veicolo a Tessie per il monitoraggio automatico del " +"contachilometri e della batteria. Quando abilitato, l'inserimento manuale" +" del contachilometri sarà disabilitato." #: app/templates/vehicles/form.html:220 -#, fuzzy msgid "Tessie VIN" -msgstr "Tessie" +msgstr "Tessie VIN" #: app/templates/vehicles/form.html:232 -#, fuzzy msgid "Lookup" -msgstr "Disconnetti" +msgstr "Ricerca" #: app/templates/vehicles/form.html:243 msgid "Enable Tessie tracking (disables manual odometer entry)" -msgstr "" +msgstr "Abilita tracciamento Tessie (disabilita inserimento manuale contachilometri)" #: app/templates/vehicles/form.html:253 -#, fuzzy msgid "Tessie tracking is active for this vehicle" -msgstr "Nessun dato Tessie disponibile per questo veicolo." +msgstr "Tracciamento Tessie attivo per questo veicolo" #: app/templates/vehicles/index.html:7 -#, fuzzy msgid "Archived Vehicles" -msgstr "Tutti i Veicoli" +msgstr "Veicoli Archiviati" #: app/templates/vehicles/index.html:8 msgid "Manage your cars, vans, motorbikes, and scooters" msgstr "" +"Gestisci le tue auto, furgoni, moto e scooter" #: app/templates/vehicles/index.html:18 -#, fuzzy msgid "Active Vehicles" -msgstr "Tutti i Veicoli" +msgstr "Veicoli Attivi" #: app/templates/vehicles/index.html:23 -#, fuzzy msgid "Archived" -msgstr "Attiva" +msgstr "Archiviati" #: app/templates/vehicles/index.html:83 -#, fuzzy msgid "Inactive" -msgstr "Attiva" +msgstr "Inattivi" #: app/templates/vehicles/index.html:100 -#, fuzzy msgid "No vehicles" -msgstr "Veicoli" +msgstr "Nessun veicolo" #: app/templates/vehicles/index.html:101 msgid "Get started by adding your first vehicle." msgstr "" +"Inizia aggiungendo il tuo primo veicolo." #: app/templates/vehicles/part_form.html:2 #: app/templates/vehicles/part_form.html:8 -#, fuzzy msgid "Edit Part" -msgstr "Modifica Viaggio" +msgstr "Modifica Ricambio" #: app/templates/vehicles/part_form.html:2 #: app/templates/vehicles/part_form.html:8 #: app/templates/vehicles/part_form.html:102 #: app/templates/vehicles/parts.html:19 app/templates/vehicles/view.html:530 -#, fuzzy msgid "Add Part" -msgstr "Ricambi" +msgstr "Aggiungi Ricambio" #: app/templates/vehicles/part_form.html:7 -#, fuzzy msgid "Back to parts" -msgstr "Torna ai viaggi" +msgstr "Torna ai Ricambi" #: app/templates/vehicles/part_form.html:15 -#, fuzzy msgid "Part Details" -msgstr "Dettagli Account" +msgstr "Dettagli Ricambio" #: app/templates/vehicles/part_form.html:19 -#, fuzzy msgid "Part Type" -msgstr "Tipo di Caricatore" +msgstr "Tipo Ricambio" #: app/templates/vehicles/part_form.html:37 -#, fuzzy msgid "Specification" -msgstr "Notifiche" +msgstr "Specifica" #: app/templates/vehicles/part_form.html:42 msgid "Oil viscosity, filter model, tire size, etc." -msgstr "" +msgstr "Viscosità olio, modello filtro, dimensione pneumatico, ecc." #: app/templates/vehicles/part_form.html:46 msgid "Quantity" -msgstr "" +msgstr "Quantità" #: app/templates/vehicles/part_form.html:54 -#, fuzzy msgid "Unit" -msgstr "Importo" +msgstr "Unità" #: app/templates/vehicles/part_form.html:57 -#, fuzzy msgid "Select unit..." -msgstr "Seleziona tipo..." +msgstr "Seleziona unità..." #: app/templates/vehicles/part_form.html:58 msgid "Liters (L)" -msgstr "" +msgstr "Litri (L)" #: app/templates/vehicles/part_form.html:59 msgid "Milliliters (ml)" -msgstr "" +msgstr "Millilitri (ml)" #: app/templates/vehicles/part_form.html:60 msgid "Gallons (gal)" -msgstr "" +msgstr "Galloni (gal)" #: app/templates/vehicles/part_form.html:61 msgid "Quarts (qt)" -msgstr "" +msgstr "Quarti (qt)" #: app/templates/vehicles/part_form.html:62 -#, fuzzy msgid "Units" -msgstr "Note" +msgstr "Unità" #: app/templates/vehicles/part_form.html:63 -#, fuzzy msgid "Pieces" -msgstr "Accesso API" +msgstr "Pezzi" #: app/templates/vehicles/part_form.html:64 -#, fuzzy msgid "Set" -msgstr "Usa TLS" +msgstr "Set" #: app/templates/vehicles/part_form.html:65 -#, fuzzy msgid "Pair" -msgstr "Ricambi" +msgstr "Coppia" #: app/templates/vehicles/part_form.html:70 msgid "Part Number" -msgstr "" +msgstr "Codice Ricambio" #: app/templates/vehicles/part_form.html:78 msgid "Supplier URL" -msgstr "" +msgstr "URL Fornitore" #: app/templates/vehicles/part_form.html:83 msgid "Link to purchase this part" -msgstr "" +msgstr "Link per acquistare questo ricambio" #: app/templates/vehicles/parts.html:2 app/templates/vehicles/parts.html:11 #: app/templates/vehicles/view.html:480 msgid "Parts & Consumables" -msgstr "" +msgstr "Ricambi e Consumabili" #: app/templates/vehicles/parts.html:47 msgid "Qty:" -msgstr "" +msgstr "Q.tà:" #: app/templates/vehicles/parts.html:94 -#, fuzzy msgid "No parts added" -msgstr "Energia Aggiunta" +msgstr "Nessun ricambio aggiunto" #: app/templates/vehicles/parts.html:95 msgid "Track the parts and consumables needed for servicing this vehicle." msgstr "" +"Tieni traccia dei ricambi e dei consumabili necessari per la manutenzione" +" di questo veicolo." #: app/templates/vehicles/parts.html:102 -#, fuzzy msgid "Add First Part" -msgstr "Aggiungi Stazione" +msgstr "Aggiungi Primo Ricambio" #: app/templates/vehicles/share.html:2 app/templates/vehicles/share.html:20 #: app/templates/vehicles/view.html:44 -#, fuzzy msgid "Share" -msgstr "Salva" +msgstr "Condividi" #: app/templates/vehicles/share.html:8 -#, fuzzy msgid "Share Vehicle" -msgstr "Tutti i Veicoli" +msgstr "Condividi Veicolo" #: app/templates/vehicles/share.html:9 msgid "Allow other users to view and add logs to this vehicle" msgstr "" +"Consenti ad altri utenti di visualizzare e aggiungere registri a questo " +"veicolo" #: app/templates/vehicles/share.html:13 -#, fuzzy msgid "Add User" -msgstr "Crea Utente" +msgstr "Aggiungi Utente" #: app/templates/vehicles/share.html:16 -#, fuzzy msgid "Enter username" -msgstr "Nome Utente SMTP" +msgstr "Inserisci nome utente" #: app/templates/vehicles/share.html:27 msgid "Shared With" -msgstr "" +msgstr "Condiviso Con" #: app/templates/vehicles/share.html:39 -#, fuzzy msgid "Remove" -msgstr "Rimuovi Logo" +msgstr "Rimuovi" #: app/templates/vehicles/share.html:45 msgid "This vehicle is not shared with anyone yet." -msgstr "" +msgstr "Questo veicolo non è ancora condiviso con nessuno." #: app/templates/vehicles/view.html:32 -#, fuzzy msgid "Download PDF Report" -msgstr "Scarica ZIP" +msgstr "Scarica Report in PDF" #: app/templates/vehicles/view.html:56 app/templates/vehicles/view.html:60 #, fuzzy msgid "Not set" -msgstr "Note" +msgstr "Non impostato" #: app/templates/vehicles/view.html:63 -#, fuzzy msgid "Last Odometer" -msgstr "Ultimo contachilometri" +msgstr "Ultimo Contachilometri" #: app/templates/vehicles/view.html:89 -#, fuzzy msgid "Avg. Consumption" -msgstr "Consumo di Carburante" +msgstr "Consumo Medio" #: app/templates/vehicles/view.html:103 msgid "Add Fuel Log" @@ -3414,11 +3329,11 @@ msgstr "Aggiorna" #: app/templates/vehicles/view.html:174 msgid "MOT Status" -msgstr "Stato MOT" +msgstr "Stato Revisione" #: app/templates/vehicles/view.html:192 msgid "MOT Expiry" -msgstr "Scadenza MOT" +msgstr "Scadenza Revisione" #: app/templates/vehicles/view.html:202 msgid "Tax Status" @@ -3473,125 +3388,118 @@ msgid "" "This vehicle is archived. It won't appear in regular lists or " "calculations." msgstr "" +"Questo veicolo è archiviato. Non apparirà negli elenchi normali o nei " +"calcoli." #: app/templates/vehicles/view.html:371 -#, fuzzy msgid "Upcoming Reminders" msgstr "Promemoria in arrivo" #: app/templates/vehicles/view.html:383 -#, fuzzy msgid "Mark as completed" -msgstr "Segna come Completato" +msgstr "Segna come completato" #: app/templates/vehicles/view.html:410 #, python-format msgid "%(days)s days overdue" -msgstr "" +msgstr "%(days)s giorni di ritardo" #: app/templates/vehicles/view.html:412 -#, fuzzy msgid "Due today" -msgstr "In Scadenza" +msgstr "Scade oggi" #: app/templates/vehicles/view.html:414 -#, fuzzy msgid "Due tomorrow" -msgstr "In Scadenza" +msgstr "Scade domani" #: app/templates/vehicles/view.html:416 #, python-format msgid "In %(days)s days" -msgstr "" +msgstr "Tra %(days)s giorni" #: app/templates/vehicles/view.html:524 msgid "No parts added yet." -msgstr "" +msgstr "Nessun ricambio aggiunto ancora." #: app/templates/vehicles/view.html:538 -#, fuzzy msgid "Fuel Consumption Trend" -msgstr "Consumo di Carburante" +msgstr "Tendenza Consumo Carburante" #: app/templates/vehicles/view.html:561 -#, fuzzy msgid "(Full)" -msgstr "Carburante" +msgstr "(Pieno)" #: app/templates/vehicles/view.html:578 -#, fuzzy msgid "Delete this fuel log?" -msgstr "Eliminare questa stazione?" +msgstr "Eliminare questo registro carburante?" #: app/templates/vehicles/view.html:593 -#, fuzzy msgid "No fuel logs yet." -msgstr "Registri Carburante" +msgstr "Nessun registro carburante ancora." #: app/templates/vehicles/view.html:622 -#, fuzzy msgid "Delete this expense?" -msgstr "Eliminare questa spesa ricorrente?" +msgstr "Eliminare questa spesa?" #: app/templates/vehicles/view.html:637 -#, fuzzy msgid "No expenses yet." -msgstr "Altre Spese" +msgstr "Nessuna spesa ancora." #: app/templates/vehicles/view.html:653 -#, fuzzy msgid "Archive this vehicle" -msgstr "Veicolo non valido" +msgstr "Archivia questo veicolo" #: app/templates/vehicles/view.html:654 msgid "" "Archived vehicles won't appear in regular lists or calculations. You can " "restore them later." msgstr "" +"I veicoli archiviati non appariranno negli elenchi normali o nei " +"calcoli. Puoi ripristinarli in seguito." #: app/templates/vehicles/view.html:656 -#, fuzzy msgid "Restore this vehicle" -msgstr "I Tuoi Veicoli" +msgstr "Ripristina questo veicolo" #: app/templates/vehicles/view.html:657 msgid "" "Restore this vehicle to make it active again and include it in " "calculations." msgstr "" +"Ripristina questo veicolo per renderlo nuovamente attivo e includerlo nei " +"calcoli." #: app/templates/vehicles/view.html:665 -#, fuzzy msgid "Archive Vehicle" -msgstr "Veicolo non valido" +msgstr "Archivia Veicolo" #: app/templates/vehicles/view.html:673 -#, fuzzy msgid "Restore Vehicle" -msgstr "I Tuoi Veicoli" +msgstr "Ripristina Veicolo" #: app/templates/vehicles/view.html:683 -#, fuzzy msgid "Delete this vehicle" -msgstr "Eliminare questo documento?" +msgstr "Elimina questo veicolo" #: app/templates/vehicles/view.html:684 msgid "" "Once you delete a vehicle, there is no going back. This will also delete " "all associated fuel logs and expenses." msgstr "" +"Una volta eliminato un veicolo, non si può tornare indietro. Questo " +"eliminerà anche tutti i registri carburante e le spese associate." #: app/templates/vehicles/view.html:687 msgid "" "Are you sure you want to delete this vehicle? This action cannot be " "undone." msgstr "" +"Sei sicuro di voler eliminare questo veicolo? Questa azione non può essere" +" annullata." #: app/templates/vehicles/view.html:691 -#, fuzzy msgid "Delete Vehicle" -msgstr "Tutti i Veicoli" +msgstr "Elimina Veicolo" #~ msgid "Make" #~ msgstr "Marca" - diff --git a/config.py b/config.py index 26da232..0de9047 100644 --- a/config.py +++ b/config.py @@ -4,7 +4,7 @@ basedir = Path(__file__).parent.absolute() -APP_VERSION = '0.21.1' +APP_VERSION = '0.22.0' RELEASE_CHANNEL = os.environ.get('RELEASE_CHANNEL', 'stable') GIT_SHA = os.environ.get('GIT_SHA', '')[:7] # Short SHA GITHUB_REPO = 'dannymcc/may' @@ -54,4 +54,4 @@ class Config: SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or f'sqlite:///{basedir}/data/may.db' SQLALCHEMY_TRACK_MODIFICATIONS = False UPLOAD_FOLDER = os.environ.get('UPLOAD_FOLDER') or str(basedir / 'data' / 'uploads') - MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB max upload + MAX_CONTENT_LENGTH = 300 * 1024 * 1024 # 300MB max upload diff --git a/migrations/versions/42b26bf6d488_add_is_shared_to_vehicles.py b/migrations/versions/42b26bf6d488_add_is_shared_to_vehicles.py new file mode 100644 index 0000000..bdecdf8 --- /dev/null +++ b/migrations/versions/42b26bf6d488_add_is_shared_to_vehicles.py @@ -0,0 +1,38 @@ +"""add is_shared to vehicles + +Revision ID: 42b26bf6d488 +Revises: f1a2b3c4d5e6 +Create Date: 2026-05-02 13:25:51.518378 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '42b26bf6d488' +down_revision = 'f1a2b3c4d5e6' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('charging_sessions', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_charging_sessions_tessie_charge_id')) + + with op.batch_alter_table('vehicles', schema=None) as batch_op: + batch_op.add_column(sa.Column('is_shared', sa.Boolean(), nullable=False, server_default=sa.false())) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('vehicles', schema=None) as batch_op: + batch_op.drop_column('is_shared') + + with op.batch_alter_table('charging_sessions', schema=None) as batch_op: + batch_op.create_index(batch_op.f('ix_charging_sessions_tessie_charge_id'), ['tessie_charge_id'], unique=1) + + # ### end Alembic commands ### diff --git a/migrations/versions/f1a2b3c4d5e6_add_annual_mileage_limit_to_vehicles.py b/migrations/versions/f1a2b3c4d5e6_add_annual_mileage_limit_to_vehicles.py new file mode 100644 index 0000000..2d6aa37 --- /dev/null +++ b/migrations/versions/f1a2b3c4d5e6_add_annual_mileage_limit_to_vehicles.py @@ -0,0 +1,39 @@ +"""add annual mileage limit to vehicles + +Revision ID: f1a2b3c4d5e6 +Revises: ee92897cc33b +Create Date: 2026-04-27 18:00:00.000000 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'f1a2b3c4d5e6' +down_revision = 'ee92897cc33b' +branch_labels = None +depends_on = None + + +def upgrade(): + bind = op.get_bind() + inspector = sa.inspect(bind) + vehicle_cols = [c['name'] for c in inspector.get_columns('vehicles')] + + cols_to_add = [] + if 'annual_mileage_limit' not in vehicle_cols: + cols_to_add.append(sa.Column('annual_mileage_limit', sa.Float(), nullable=True)) + if 'annual_mileage_start_date' not in vehicle_cols: + cols_to_add.append(sa.Column('annual_mileage_start_date', sa.Date(), nullable=True)) + + if cols_to_add: + with op.batch_alter_table('vehicles', schema=None) as batch_op: + for col in cols_to_add: + batch_op.add_column(col) + + +def downgrade(): + with op.batch_alter_table('vehicles', schema=None) as batch_op: + batch_op.drop_column('annual_mileage_start_date') + batch_op.drop_column('annual_mileage_limit') diff --git a/requirements.txt b/requirements.txt index 668c8e1..4597bb3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,16 +1,16 @@ flask>=3.0.0 flask-sqlalchemy>=3.1.1 -flask-migrate>=4.0.0 +flask-migrate>=4.1.0 flask-login>=0.6.3 flask-wtf>=1.2.1 flask-babel>=4.0.0 -werkzeug>=3.0.1 +werkzeug>=3.1.8 python-dotenv>=1.0.0 pillow>=10.4.0 -gunicorn>=21.2.0 +gunicorn>=25.3.0 weasyprint>=62.0 -requests>=2.31.0 -python-dateutil>=2.8.2 +requests>=2.33.1 +python-dateutil>=2.9.0.post0 pytest>=8.0.0 pytest-cov>=4.1.0 coverage>=7.0.0 diff --git a/tests/test_expenses.py b/tests/test_expenses.py index 37b219b..19ae998 100644 --- a/tests/test_expenses.py +++ b/tests/test_expenses.py @@ -1,6 +1,7 @@ import pytest +from datetime import date from app import db -from app.models import Expense +from app.models import Expense, EXPENSE_CATEGORIES class TestExpenseIndex: @@ -98,3 +99,56 @@ def test_delete_expense(self, auth_client, sample_expense): resp = auth_client.post(f'/expenses/{expense_id}/delete', follow_redirects=True) assert resp.status_code == 200 assert Expense.query.get(expense_id) is None + + +class TestExpenseCategories: + def test_inspection_category_exists(self): + category_keys = [k for k, _ in EXPENSE_CATEGORIES] + assert 'inspection' in category_keys + + def test_create_inspection_expense(self, auth_client, sample_vehicle): + resp = auth_client.post('/expenses/new', data={ + 'vehicle_id': str(sample_vehicle.id), + 'date': '2024-03-01', + 'category': 'inspection', + 'description': 'Annual MOT inspection', + 'cost': '55.00', + }, follow_redirects=True) + assert resp.status_code == 200 + expense = Expense.query.filter_by(description='Annual MOT inspection').first() + assert expense is not None + assert expense.category == 'inspection' + + def test_expense_list_shows_odometer(self, auth_client, test_user, sample_vehicle): + expense = Expense( + vehicle_id=sample_vehicle.id, + user_id=test_user.id, + date=date(2024, 3, 1), + category='maintenance', + description='Oil change with odometer', + cost=40.0, + odometer=12345.0, + ) + db.session.add(expense) + db.session.commit() + resp = auth_client.get('/expenses/') + assert resp.status_code == 200 + assert b'12345' in resp.data + + def test_expense_list_shows_expandable_details(self, auth_client, test_user, sample_vehicle): + expense = Expense( + vehicle_id=sample_vehicle.id, + user_id=test_user.id, + date=date(2024, 3, 2), + category='repairs', + description='Brake pads', + cost=120.0, + vendor='AutoShop Ltd', + notes='Front brakes replaced', + ) + db.session.add(expense) + db.session.commit() + resp = auth_client.get('/expenses/') + assert resp.status_code == 200 + assert b'AutoShop Ltd' in resp.data + assert b'Front brakes replaced' in resp.data diff --git a/tests/test_vehicles.py b/tests/test_vehicles.py index 2f4bcdd..10f44de 100644 --- a/tests/test_vehicles.py +++ b/tests/test_vehicles.py @@ -123,3 +123,85 @@ def test_archived_vehicles_shown_with_param(self, auth_client, sample_vehicle): resp = auth_client.get('/vehicles/?archived=true') assert resp.status_code == 200 assert b'Test Car' in resp.data + + +class TestVehicleSharing: + def test_is_shared_defaults_false(self, sample_vehicle): + assert sample_vehicle.is_shared is False + + def test_shared_vehicle_visible_to_other_user(self, app, test_user, sample_vehicle): + from app.models import User + other = User(username='other_user', email='other@example.com') + other.set_password('OtherPass123!') + db.session.add(other) + db.session.commit() + + # Not shared yet — other user should not see it + assert sample_vehicle not in other.get_all_vehicles() + + # Mark as shared + sample_vehicle.is_shared = True + db.session.commit() + + assert sample_vehicle in other.get_all_vehicles() + + def test_edit_vehicle_sets_is_shared(self, auth_client, sample_vehicle): + resp = auth_client.post(f'/vehicles/{sample_vehicle.id}/edit', data={ + 'name': sample_vehicle.name, + 'vehicle_type': sample_vehicle.vehicle_type, + 'fuel_type': sample_vehicle.fuel_type, + 'tracking_unit': 'mileage', + 'is_active': 'on', + 'is_shared': 'on', + }, follow_redirects=True) + assert resp.status_code == 200 + db.session.refresh(sample_vehicle) + assert sample_vehicle.is_shared is True + + def test_edit_vehicle_clears_is_shared(self, auth_client, sample_vehicle): + sample_vehicle.is_shared = True + db.session.commit() + + resp = auth_client.post(f'/vehicles/{sample_vehicle.id}/edit', data={ + 'name': sample_vehicle.name, + 'vehicle_type': sample_vehicle.vehicle_type, + 'fuel_type': sample_vehicle.fuel_type, + 'tracking_unit': 'mileage', + 'is_active': 'on', + # is_shared omitted → checkbox unchecked + }, follow_redirects=True) + assert resp.status_code == 200 + db.session.refresh(sample_vehicle) + assert sample_vehicle.is_shared is False + + def test_shared_badge_shown_in_vehicle_list(self, auth_client, sample_vehicle): + sample_vehicle.is_shared = True + db.session.commit() + resp = auth_client.get('/vehicles/') + assert resp.status_code == 200 + assert b'Shared' in resp.data + + +class TestVehicleViewMaintenancePanel: + def test_view_shows_maintenance_panel(self, auth_client, app, test_user, sample_vehicle): + from app.models import MaintenanceSchedule + from datetime import date, timedelta + schedule = MaintenanceSchedule( + vehicle_id=sample_vehicle.id, + user_id=test_user.id, + name='Oil Change', + maintenance_type='oil_change', + next_due_date=date.today() + timedelta(days=15), + is_active=True, + ) + db.session.add(schedule) + db.session.commit() + resp = auth_client.get(f'/vehicles/{sample_vehicle.id}') + assert resp.status_code == 200 + assert b'Upcoming Maintenance' in resp.data + assert b'Oil Change' in resp.data + + def test_view_hides_maintenance_panel_when_empty(self, auth_client, sample_vehicle): + resp = auth_client.get(f'/vehicles/{sample_vehicle.id}') + assert resp.status_code == 200 + assert b'Upcoming Maintenance' not in resp.data