Initial commit — MarineMaintenance v1.0

Marine maintenance management: work orders with photos, ISM/SWP procedures,
MSDS, inventory, RFQ/purchases, vessel history, bilingual PDF reports.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-05 01:54:20 -04:00
commit 67a0e674ca
44 changed files with 8439 additions and 0 deletions
+92
View File
@@ -0,0 +1,92 @@
{% extends 'base.html' %}
{% block title %}Compra #{{ purchase.id }}{% endblock %}
{% block page_title %}Compra — {{ purchase.purchase_date }}{% endblock %}
{% block topbar_actions %}<a href="{{ url_for('purchases') }}" class="btn btn-secondary">← Volver</a>{% endblock %}
{% block content %}
<div class="grid-2 mb-4">
<div class="card">
<div class="card-header">📋 Detalles</div>
<table style="font-size:13px">
<tr><td style="color:var(--gray);padding:6px 0;width:140px">Proveedor</td><td>{{ purchase.supplier_name or '—' }}</td></tr>
<tr><td style="color:var(--gray);padding:6px 0">Factura</td><td>{{ purchase.invoice_number or '—' }}</td></tr>
<tr><td style="color:var(--gray);padding:6px 0">Fecha</td><td>{{ purchase.purchase_date }}</td></tr>
<tr><td style="color:var(--gray);padding:6px 0">Total</td><td class="text-cyan">${{ "%.2f"|format(purchase.total_amount or 0) }}</td></tr>
</table>
{% if purchase.notes %}<p style="font-size:13px;color:var(--gray);margin-top:10px">{{ purchase.notes }}</p>{% endif %}
</div>
</div>
<div class="card">
<div class="card-header flex justify-between">
<span>📦 Ítems Comprados</span>
<button onclick="document.getElementById('itemModal').style.display='flex'" class="btn btn-sm btn-primary">+ Agregar Ítem</button>
</div>
<div class="table-wrap">
<table>
<thead><tr><th>Repuesto</th><th>Descripción</th><th>Cantidad</th><th>Costo Unit.</th><th>Total</th></tr></thead>
<tbody>
{% for i in items %}
<tr>
<td>{{ i.part_name or '—' }}</td>
<td class="text-gray">{{ i.description or '' }}</td>
<td>{{ i.quantity }}</td>
<td>${{ "%.2f"|format(i.unit_cost) }}</td>
<td class="text-cyan">${{ "%.2f"|format(i.total_cost) }}</td>
</tr>
{% else %}
<tr><td colspan="5" class="text-gray" style="text-align:center;padding:20px">Sin ítems.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div id="itemModal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.8);z-index:1000;align-items:center;justify-content:center">
<div class="card" style="width:460px;max-width:95vw">
<div class="card-header">📦 Agregar Ítem</div>
<div class="form-group mb-4">
<label>Repuesto del Inventario</label>
<select id="itemPart">
<option value="">-- Sin inventario (manual) --</option>
{% for p in parts %}
<option value="{{ p.id }}">{{ p.name }} {% if p.part_number %}({{ p.part_number }}){% endif %}</option>
{% endfor %}
</select>
</div>
<div class="form-group mb-4">
<label>Descripción (si es manual)</label>
<input type="text" id="itemDesc">
</div>
<div class="form-grid mb-4">
<div class="form-group">
<label>Cantidad</label>
<input type="number" id="itemQty" value="1" step="0.01">
</div>
<div class="form-group">
<label>Costo Unitario ($)</label>
<input type="number" id="itemCost" value="0" step="0.01">
</div>
</div>
<div class="flex gap-3">
<button onclick="addItem()" class="btn btn-primary"> Agregar</button>
<button onclick="document.getElementById('itemModal').style.display='none'" class="btn btn-secondary">Cancelar</button>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
function addItem() {
const data = {
part_id: document.getElementById('itemPart').value || null,
description: document.getElementById('itemDesc').value,
quantity: document.getElementById('itemQty').value,
unit_cost: document.getElementById('itemCost').value
};
fetch(`/purchases/{{ purchase.id }}/add-item`, {
method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify(data)
}).then(() => location.reload());
}
</script>
{% endblock %}