changes of rate model of sub-cont and there functions
This commit is contained in:
@@ -4,7 +4,7 @@ from flask import (
|
||||
request,
|
||||
redirect,
|
||||
url_for,
|
||||
flash
|
||||
flash, jsonify
|
||||
)
|
||||
|
||||
from app.models.subcontractor_model import Subcontractor
|
||||
@@ -27,25 +27,7 @@ def engineering_master():
|
||||
title="Engineering Masters"
|
||||
)
|
||||
|
||||
|
||||
@engi_bp.route("/subcontractor-rate")
|
||||
def add_subcontractor_rates():
|
||||
subcontractors = Subcontractor.query.filter_by(status="Active").all()
|
||||
|
||||
if request.method == "POST":
|
||||
try:
|
||||
SubcontractorRateService.save_rate(request.form)
|
||||
flash(SuccessMessage.SAVE, "success")
|
||||
return redirect(url_for("engineering.add_subcontractor_rates"))
|
||||
except Exception as e:
|
||||
flash(ErrorMessage.INTERNAL_SERVER_ERROR, "danger")
|
||||
|
||||
return render_template(
|
||||
"engineering/add_rate.html",
|
||||
title="Subcontractor Rate Master",
|
||||
subcontractors=subcontractors
|
||||
)
|
||||
|
||||
# Client rate model
|
||||
@engi_bp.route("/client-rate")
|
||||
def client_rates():
|
||||
|
||||
@@ -54,3 +36,72 @@ def client_rates():
|
||||
title="Client Rate Master"
|
||||
)
|
||||
|
||||
@engi_bp.route("/subcontractor-rate", methods=["GET", "POST"])
|
||||
def add_subcontractor_rates():
|
||||
|
||||
subcontractors = Subcontractor.query.filter_by(status="Active").all()
|
||||
|
||||
if request.method == "POST":
|
||||
result = SubcontractorRateService.save_or_update(request.form)
|
||||
if result["success"]:
|
||||
flash(result["message"], "success")
|
||||
return redirect(url_for("engineering.add_subcontractor_rates"))
|
||||
else:
|
||||
flash(result["message"], "danger")
|
||||
|
||||
rates = SubcontractorRateService.get_all_rates()
|
||||
|
||||
return render_template(
|
||||
"engineering/contractor_rate.html",
|
||||
title="Subcontractor Rate Master",
|
||||
subcontractors=subcontractors,
|
||||
rates=rates
|
||||
)
|
||||
|
||||
@engi_bp.route("/subcontractor-rate/edit/<int:rate_id>", methods=["GET", "POST"])
|
||||
def edit_rate(rate_id):
|
||||
|
||||
subcontractors = Subcontractor.query.filter_by(status="Active").all()
|
||||
|
||||
if request.method == "POST":
|
||||
|
||||
result = SubcontractorRateService.save_or_update(request.form)
|
||||
|
||||
if result["success"]:
|
||||
flash(result["message"], "success")
|
||||
return redirect(url_for("engineering.add_subcontractor_rates"))
|
||||
|
||||
flash(result["message"], "danger")
|
||||
|
||||
rate = SubcontractorRateService.get_rate(rate_id)
|
||||
|
||||
rates = SubcontractorRateService.get_all_rates()
|
||||
|
||||
return render_template(
|
||||
"engineering/contractor_rate.html",
|
||||
subcontractors=subcontractors,
|
||||
rate=rate,
|
||||
rates=rates
|
||||
)
|
||||
|
||||
@engi_bp.route("/subcontractor-rate/delete/<int:rate_id>")
|
||||
def delete_rate(rate_id):
|
||||
SubcontractorRateService.delete_rate(rate_id)
|
||||
flash("Rate deleted successfully.", "success")
|
||||
return redirect(url_for("engineering.add_subcontractor_rates"))
|
||||
|
||||
|
||||
@engi_bp.route("/check-rate")
|
||||
def check_rate():
|
||||
|
||||
exists = SubcontractorRateService.check_duplicate(
|
||||
subcontractor_id=request.args.get("subcontractor_id"),
|
||||
category=request.args.get("category"),
|
||||
item_name=request.args.get("item_name"),
|
||||
rate_id=request.args.get("rate_id")
|
||||
)
|
||||
|
||||
return jsonify({
|
||||
"exists": exists
|
||||
})
|
||||
|
||||
|
||||
@@ -1,28 +1,66 @@
|
||||
from app.services.db_service import db
|
||||
from app.models.subcontractor_rate_model import SubcontractorRate
|
||||
from sqlalchemy import func
|
||||
|
||||
|
||||
class SubcontractorRateService:
|
||||
|
||||
@staticmethod
|
||||
def save_rate(form):
|
||||
def save_or_update(form):
|
||||
|
||||
rate = SubcontractorRate(
|
||||
subcontractor_id=form.get("subcontractor_id"),
|
||||
category=form.get("category"),
|
||||
item_code=form.get("item_code"),
|
||||
item_name=form.get("item_name"),
|
||||
unit=form.get("unit"),
|
||||
rate=form.get("rate"),
|
||||
effective_from=form.get("effective_from"),
|
||||
effective_to=form.get("effective_to") or None,
|
||||
status=form.get("status")
|
||||
rate_id = form.get("id")
|
||||
|
||||
subcontractor_id = form.get("subcontractor_id")
|
||||
category = form.get("category")
|
||||
item_name = form.get("item_name").strip()
|
||||
|
||||
# -----------------------------
|
||||
# Duplicate Validation
|
||||
# -----------------------------
|
||||
duplicate = (
|
||||
SubcontractorRate.query
|
||||
.filter(
|
||||
SubcontractorRate.subcontractor_id == subcontractor_id,
|
||||
SubcontractorRate.category == category,
|
||||
func.lower(SubcontractorRate.item_name) == item_name.lower()
|
||||
)
|
||||
.first()
|
||||
)
|
||||
|
||||
db.session.add(rate)
|
||||
db.session.commit()
|
||||
return rate
|
||||
# Ignore current record while editing
|
||||
if duplicate and (not rate_id or duplicate.id != int(rate_id)):
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Category and Rate already exists for this Subcontractor."
|
||||
}
|
||||
|
||||
# -----------------------------
|
||||
# Insert / Update
|
||||
# -----------------------------
|
||||
if rate_id:
|
||||
rate = SubcontractorRate.query.get_or_404(rate_id)
|
||||
else:
|
||||
rate = SubcontractorRate()
|
||||
|
||||
rate.subcontractor_id = subcontractor_id
|
||||
rate.category = category
|
||||
rate.item_code = form.get("item_code")
|
||||
rate.item_name = item_name
|
||||
rate.unit = form.get("unit")
|
||||
rate.rate = form.get("rate")
|
||||
rate.effective_from = form.get("effective_from")
|
||||
rate.effective_to = form.get("effective_to") or None
|
||||
rate.status = form.get("status")
|
||||
|
||||
if not rate_id:
|
||||
db.session.add(rate)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Saved Successfully."
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_all_rates():
|
||||
@@ -32,34 +70,33 @@ class SubcontractorRateService:
|
||||
.order_by(SubcontractorRate.created_at.desc())
|
||||
.all()
|
||||
)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_rate(rate_id):
|
||||
|
||||
return SubcontractorRate.query.get_or_404(rate_id)
|
||||
|
||||
@staticmethod
|
||||
def update_rate(rate_id, form):
|
||||
|
||||
rate = SubcontractorRate.query.get_or_404(rate_id)
|
||||
|
||||
rate.subcontractor_id = form.get("subcontractor_id")
|
||||
rate.category = form.get("category")
|
||||
rate.item_code = form.get("item_code")
|
||||
rate.item_name = form.get("item_name")
|
||||
rate.unit = form.get("unit")
|
||||
rate.rate = form.get("rate")
|
||||
rate.effective_from = form.get("effective_from")
|
||||
rate.effective_to = form.get("effective_to") or None
|
||||
rate.status = form.get("status")
|
||||
|
||||
db.session.commit()
|
||||
return rate
|
||||
|
||||
@staticmethod
|
||||
def delete_rate(rate_id):
|
||||
|
||||
rate = SubcontractorRate.query.get_or_404(rate_id)
|
||||
|
||||
db.session.delete(rate)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
|
||||
@staticmethod
|
||||
def check_duplicate(subcontractor_id, category, item_name, rate_id=None):
|
||||
|
||||
query = SubcontractorRate.query.filter(
|
||||
SubcontractorRate.subcontractor_id == subcontractor_id,
|
||||
SubcontractorRate.category == category,
|
||||
func.lower(SubcontractorRate.item_name) == item_name.strip().lower()
|
||||
)
|
||||
|
||||
if rate_id:
|
||||
query = query.filter(SubcontractorRate.id != int(rate_id))
|
||||
|
||||
return query.first() is not None
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -128,6 +128,13 @@
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<!-- Formats -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/file_format">
|
||||
<i class="bi bi-file-earmark-text me-1"></i> Formats
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- Masters -->
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="/engi">
|
||||
@@ -144,17 +151,9 @@
|
||||
<!-- Client Standard Rates -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/engi/client-rate">
|
||||
<i class="bi bi-file-earmark-text me-1"></i> Client Standard Rates
|
||||
<i class="bi bi-file-earmark-text me-1"></i> Client Rates
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- Formats -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/file_format">
|
||||
<i class="bi bi-file-earmark-text me-1"></i> Formats
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -185,10 +184,10 @@
|
||||
</small>
|
||||
</li>
|
||||
|
||||
<!-- Dashboard -->
|
||||
<!-- Masters -->
|
||||
<li>
|
||||
<a class="dropdown-item text-light py-2" href="{{ url_for('dashboard.dashboard') }}">
|
||||
<i class="bi bi-speedometer2 me-2"></i> Dashboard
|
||||
<a class="dropdown-item text-light py-2" href="{{ url_for('engineering.engineering_master') }}">
|
||||
<i class="bi bi-gear me-2"></i> Masters
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -199,12 +198,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- Manage Account -->
|
||||
<!-- Manage Account
|
||||
<li>
|
||||
<a class="dropdown-item py-2" href="#">
|
||||
<i class="bi bi-gear me-2"></i> Manage Account
|
||||
</a>
|
||||
</li>
|
||||
-->
|
||||
|
||||
<li><hr class="dropdown-divider border-secondary m-0"></li>
|
||||
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="container-fluid mt-4">
|
||||
|
||||
<div class="card shadow">
|
||||
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h4 class="mb-0">
|
||||
<i class="bi bi-currency-rupee"></i>
|
||||
Subcontractor Rate Master
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<form method="POST">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<!-- Subcontractor -->
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label fw-bold">
|
||||
Subcontractor
|
||||
</label>
|
||||
|
||||
<select name="subcontractor_id"
|
||||
class="form-select"
|
||||
required>
|
||||
|
||||
<option value="">
|
||||
Select Subcontractor
|
||||
</option>
|
||||
|
||||
{% for sub in subcontractors %}
|
||||
<option value="{{ sub.id }}">
|
||||
{{ sub.subcontractor_name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Category -->
|
||||
<div class="col-md-4 mb-3">
|
||||
|
||||
<label class="form-label fw-bold">
|
||||
Category
|
||||
</label>
|
||||
|
||||
<select name="category"
|
||||
class="form-select"
|
||||
required>
|
||||
|
||||
<option value="">
|
||||
Select Category
|
||||
</option>
|
||||
|
||||
<option value="TR_EX">
|
||||
Trench Excavation
|
||||
</option>
|
||||
|
||||
<option value="MH_EX">
|
||||
Manhole Excavation
|
||||
</option>
|
||||
|
||||
<option value="MH_DC">
|
||||
Manhole & Domestic Chamber
|
||||
</option>
|
||||
|
||||
<option value="LAYING">
|
||||
Pipe Laying
|
||||
</option>
|
||||
|
||||
</select>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Status -->
|
||||
<div class="col-md-4 mb-3">
|
||||
|
||||
<label class="form-label fw-bold">
|
||||
Status
|
||||
</label>
|
||||
|
||||
<select name="status"
|
||||
class="form-select">
|
||||
|
||||
<option value="Active">
|
||||
Active
|
||||
</option>
|
||||
|
||||
<option value="Inactive">
|
||||
Inactive
|
||||
</option>
|
||||
|
||||
</select>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<!-- Item Code -->
|
||||
<div class="col-md-3 mb-3">
|
||||
|
||||
<label class="form-label fw-bold">
|
||||
Item Code
|
||||
</label>
|
||||
|
||||
<input type="text"
|
||||
name="item_code"
|
||||
class="form-control"
|
||||
placeholder="SM01"
|
||||
required>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Item Name -->
|
||||
<div class="col-md-5 mb-3">
|
||||
|
||||
<label class="form-label fw-bold">
|
||||
Item Name
|
||||
</label>
|
||||
|
||||
<input type="text"
|
||||
name="item_name"
|
||||
class="form-control"
|
||||
placeholder="Soft Murum 0-1.5"
|
||||
required>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Unit -->
|
||||
<div class="col-md-2 mb-3">
|
||||
|
||||
<label class="form-label fw-bold">
|
||||
Unit
|
||||
</label>
|
||||
|
||||
<select name="unit"
|
||||
class="form-select">
|
||||
|
||||
<option value="Cum">Cum</option>
|
||||
<option value="Nos">Nos</option>
|
||||
<option value="Rmt">Rmt</option>
|
||||
<option value="Sqm">Sqm</option>
|
||||
|
||||
</select>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Rate -->
|
||||
<div class="col-md-2 mb-3">
|
||||
|
||||
<label class="form-label fw-bold">
|
||||
Rate (₹)
|
||||
</label>
|
||||
|
||||
<input type="number"
|
||||
step="0.01"
|
||||
min="0"
|
||||
name="rate"
|
||||
class="form-control"
|
||||
placeholder="0.00"
|
||||
required>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<!-- Effective From -->
|
||||
<div class="col-md-3 mb-3">
|
||||
|
||||
<label class="form-label fw-bold">
|
||||
Effective From
|
||||
</label>
|
||||
|
||||
<input type="date"
|
||||
name="effective_from"
|
||||
class="form-control"
|
||||
required>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Effective To -->
|
||||
<div class="col-md-3 mb-3">
|
||||
|
||||
<label class="form-label fw-bold">
|
||||
Effective To
|
||||
</label>
|
||||
|
||||
<input type="date"
|
||||
name="effective_to"
|
||||
class="form-control">
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="text-end">
|
||||
|
||||
<button type="reset"
|
||||
class="btn btn-secondary">
|
||||
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
|
||||
Reset
|
||||
|
||||
</button>
|
||||
|
||||
<button type="submit"
|
||||
class="btn btn-success">
|
||||
|
||||
<i class="bi bi-check-circle"></i>
|
||||
|
||||
Save Rate
|
||||
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
20
app/templates/engineering/client_rate.html
Normal file
20
app/templates/engineering/client_rate.html
Normal file
@@ -0,0 +1,20 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
<div class="container-fluid mt-4">
|
||||
|
||||
<div class="card shadow">
|
||||
|
||||
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
||||
|
||||
<h4 class="mb-0">
|
||||
<i class="bi bi-currency-rupee"></i>
|
||||
Client Rate Master
|
||||
</h4>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
384
app/templates/engineering/contractor_rate.html
Normal file
384
app/templates/engineering/contractor_rate.html
Normal file
@@ -0,0 +1,384 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
<div class="container-fluid mt-4">
|
||||
|
||||
<div class="card shadow">
|
||||
|
||||
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
||||
|
||||
<h4 class="mb-0">
|
||||
<i class="bi bi-currency-rupee"></i>
|
||||
Subcontractor Rate Master
|
||||
</h4>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<form method="POST" id="rateForm">
|
||||
|
||||
<!-- Hidden ID -->
|
||||
<input type="hidden" id="rate_id" name="id" value="{{ rate.id if rate else '' }}">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<!-- Subcontractor -->
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label fw-bold"> Subcontractor </label>
|
||||
<select id="subcontractor_id" name="subcontractor_id" class="form-select" required>
|
||||
<option value="">-- Select Subcontractor --</option>
|
||||
{% for sub in subcontractors %}
|
||||
<option value="{{ sub.id }}"
|
||||
{% if rate and rate.subcontractor_id==sub.id %}
|
||||
selected
|
||||
{% endif %}>
|
||||
{{ sub.subcontractor_name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Category -->
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label fw-bold"> Category </label>
|
||||
<select id="category" name="category" class="form-select" required>
|
||||
<option value="">-- Select Category --</option>
|
||||
|
||||
<option value="trench_excavation"
|
||||
{% if rate and rate.category=="trench_excavation" %}selected{% endif %}>
|
||||
Trench Excavation
|
||||
</option>
|
||||
<option value="manhole_excavation"
|
||||
{% if rate and rate.category=="manhole_excavation" %}selected{% endif %}>
|
||||
Manhole Excavation
|
||||
</option>
|
||||
|
||||
<option value="Manhole_Domestic_Chamber"
|
||||
{% if rate and rate.category=="Manhole_Domestic_Chamber" %}selected{% endif %}>
|
||||
MH & Domestic Chamber
|
||||
</option>
|
||||
|
||||
<option value="Laying"
|
||||
{% if rate and rate.category=="Laying" %}selected{% endif %}>
|
||||
Pipe Laying
|
||||
</option>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Status -->
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label fw-bold"> Status </label>
|
||||
|
||||
<select name="status" class="form-select">
|
||||
<option value="Active"
|
||||
{% if not rate or rate.status=="Active" %}selected{% endif %}>
|
||||
Active
|
||||
</option>
|
||||
|
||||
<option value="Inactive"
|
||||
{% if rate and rate.status=="Inactive" %}selected{% endif %}>
|
||||
Inactive
|
||||
</option>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<!-- Item Code -->
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label fw-bold"> Item Code </label>
|
||||
<input type="text" name="item_code" class="form-control" value="{{ rate.item_code if rate else '' }}" required>
|
||||
</div>
|
||||
|
||||
<!-- Item Name -->
|
||||
<div class="col-md-5 mb-3">
|
||||
<label class="form-label fw-bold"> Item Name </label>
|
||||
<input type="text"
|
||||
id="item_name"
|
||||
name="item_name"
|
||||
class="form-control"
|
||||
autocomplete="off"
|
||||
value="{{ rate.item_name if rate else '' }}"
|
||||
required>
|
||||
|
||||
<small id="duplicateMessage"
|
||||
class="text-danger"
|
||||
style="display:none;">
|
||||
</small>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Unit -->
|
||||
<div class="col-md-2 mb-3">
|
||||
<label class="form-label fw-bold">Unit</label>
|
||||
<select name="unit" class="form-select">
|
||||
<option value="Cum"
|
||||
{% if not rate or rate.unit=="Cum" %}selected{% endif %}>
|
||||
Cum
|
||||
</option>
|
||||
<option value="Nos"
|
||||
{% if rate and rate.unit=="Nos" %}selected{% endif %}>
|
||||
Nos
|
||||
</option>
|
||||
<option value="Rmt"
|
||||
{% if rate and rate.unit=="Rmt" %}selected{% endif %}>
|
||||
Rmt
|
||||
</option>
|
||||
<option value="Sqm"
|
||||
{% if rate and rate.unit=="Sqm" %}selected{% endif %}>
|
||||
Sqm
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Rate -->
|
||||
<div class="col-md-2 mb-3">
|
||||
<label class="form-label fw-bold">Rate</label>
|
||||
<input type="number" step="0.01" name="rate" class="form-control" value="{{ rate.rate if rate else '' }}" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label fw-bold"> Effective From </label>
|
||||
<input type="date" name="effective_from" class="form-control" value="{{ rate.effective_from if rate else '' }}" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label fw-bold"> Effective To </label>
|
||||
<input type="date" name="effective_to" class="form-control" value="{{ rate.effective_to if rate else '' }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="text-end">
|
||||
<a href="{{ url_for('engineering.add_subcontractor_rates') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-clockwise"></i> Reset
|
||||
</a>
|
||||
|
||||
<button type="submit" id="saveBtn" class="btn btn-success">
|
||||
{% if rate %}
|
||||
<i class="bi bi-pencil-square"></i>
|
||||
Update Rate
|
||||
{% else %}
|
||||
<i class="bi bi-check-circle"></i>
|
||||
Save Rate
|
||||
{% endif %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Table -->
|
||||
<div class="card shadow mt-4">
|
||||
<div class="card-header bg-dark text-white">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-table"></i>Rate List
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered table-hover table-striped" id="rateTable">
|
||||
<thead class="table-primary">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Subcontractor</th>
|
||||
<th>Category</th>
|
||||
<th>Item Code</th>
|
||||
<th>Item Name</th>
|
||||
<th>Unit</th>
|
||||
<th>Rate</th>
|
||||
<th>Status</th>
|
||||
<th width="130">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{% for row in rates %}
|
||||
<tr>
|
||||
<td>{{ loop.index }}</td>
|
||||
<td>{{ row.subcontractor.subcontractor_name }}</td>
|
||||
<td>{{ row.category }}</td>
|
||||
<td>{{ row.item_code }}</td>
|
||||
<td>{{ row.item_name }}</td>
|
||||
<td>{{ row.unit }}</td>
|
||||
<td>{{ row.rate }}</td>
|
||||
<td>
|
||||
{% if row.status=="Active" %}
|
||||
<span class="badge bg-success">
|
||||
Active
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="badge bg-danger">
|
||||
Inactive
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="{{ url_for('engineering.edit_rate', rate_id=row.id) }}" class="btn btn-warning btn-sm">
|
||||
<i class="bi bi-pencil-square"></i>
|
||||
</a>
|
||||
|
||||
<a href="{{ url_for('engineering.delete_rate', rate_id=row.id) }}"
|
||||
class="btn btn-danger btn-sm" onclick="return confirm('Delete this Item:{{row.item_name}} & Rate:{{row.rate}} ?')">
|
||||
<i class="bi bi-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
// ----------------------------
|
||||
// DataTable
|
||||
// ----------------------------
|
||||
$("#rateTable").DataTable({
|
||||
responsive: true,
|
||||
pageLength: 10,
|
||||
destroy: true
|
||||
});
|
||||
|
||||
// ----------------------------
|
||||
// Validate on field change
|
||||
// ----------------------------
|
||||
$("#subcontractor_id, #category").on("change", function () {
|
||||
clearValidation();
|
||||
checkDuplicateRate();
|
||||
|
||||
});
|
||||
|
||||
// ----------------------------
|
||||
// Validate while typing
|
||||
// ----------------------------
|
||||
let timer;
|
||||
|
||||
$("#item_name").on("keyup input", function () {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(function () {
|
||||
checkDuplicateRate();
|
||||
}, 300);
|
||||
|
||||
});
|
||||
|
||||
// Edit Mode
|
||||
checkDuplicateRate();
|
||||
|
||||
// ----------------------------
|
||||
// Prevent Submit
|
||||
// ----------------------------
|
||||
$("#rateForm").submit(function (e) {
|
||||
|
||||
if ($("#saveBtn").prop("disabled")) {
|
||||
e.preventDefault();
|
||||
$("#item_name").focus();
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
//=========================================
|
||||
// Reset Validation
|
||||
//=========================================
|
||||
function clearValidation() {
|
||||
$("#duplicateMessage")
|
||||
.hide()
|
||||
.text("")
|
||||
.removeClass("text-success text-danger");
|
||||
|
||||
$("#item_name")
|
||||
.removeClass("is-valid is-invalid");
|
||||
$("#saveBtn").prop("disabled", false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========================================
|
||||
// Duplicate Validation
|
||||
//=========================================
|
||||
function checkDuplicateRate() {
|
||||
let subcontractor = $("#subcontractor_id").val();
|
||||
let category = $("#category").val();
|
||||
let item_name = $("#item_name").val().trim();
|
||||
let rate_id = $("#rate_id").val();
|
||||
|
||||
// Mandatory fields
|
||||
if (
|
||||
subcontractor == "" ||
|
||||
category == "" ||
|
||||
item_name.length < 2
|
||||
) {
|
||||
clearValidation();
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
|
||||
url: "{{ url_for('engineering.check_rate') }}",
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
data: {
|
||||
subcontractor_id: subcontractor,
|
||||
category: category,
|
||||
item_name: item_name,
|
||||
rate_id: rate_id
|
||||
},
|
||||
|
||||
success: function (response) {
|
||||
if (response.exists) {
|
||||
$("#duplicateMessage")
|
||||
.text("This Item Name already exists for the selected Subcontractor and Category.")
|
||||
.removeClass("text-success")
|
||||
.addClass("text-danger")
|
||||
.show();
|
||||
|
||||
$("#item_name")
|
||||
.removeClass("is-valid")
|
||||
.addClass("is-invalid");
|
||||
|
||||
$("#saveBtn").prop("disabled", true);
|
||||
|
||||
}
|
||||
else {
|
||||
$("#duplicateMessage")
|
||||
.text("Item Name is available.")
|
||||
.removeClass("text-danger")
|
||||
.addClass("text-success")
|
||||
.show();
|
||||
|
||||
$("#item_name")
|
||||
.removeClass("is-invalid")
|
||||
.addClass("is-valid");
|
||||
|
||||
$("#saveBtn").prop("disabled", false);
|
||||
}
|
||||
},
|
||||
|
||||
error: function () {
|
||||
clearValidation();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user