complete first version of main proccess
This commit is contained in:
parent
6ff4740d04
commit
f2fc2362a7
61 changed files with 3280 additions and 28 deletions
255
installations/views.py
Normal file
255
installations/views.py
Normal file
|
@ -0,0 +1,255 @@
|
|||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import messages
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from accounts.models import Profile
|
||||
from common.consts import UserRoles
|
||||
from processes.models import ProcessInstance, StepInstance
|
||||
from invoices.models import Item, Quote, QuoteItem
|
||||
from .models import InstallationAssignment, InstallationReport, InstallationPhoto, InstallationItemChange
|
||||
from decimal import Decimal, InvalidOperation
|
||||
|
||||
@login_required
|
||||
def installation_assign_step(request, instance_id, step_id):
|
||||
instance = get_object_or_404(ProcessInstance, id=instance_id)
|
||||
step = get_object_or_404(instance.process.steps, id=step_id)
|
||||
previous_step = instance.process.steps.filter(order__lt=step.order).last()
|
||||
next_step = instance.process.steps.filter(order__gt=step.order).first()
|
||||
|
||||
# Installers list (profiles that have installer role)
|
||||
installers = Profile.objects.filter(roles__slug=UserRoles.INSTALLER.value).select_related('user').all()
|
||||
assignment, _ = InstallationAssignment.objects.get_or_create(process_instance=instance)
|
||||
|
||||
if request.method == 'POST':
|
||||
installer_id = request.POST.get('installer_id')
|
||||
scheduled_date = (request.POST.get('scheduled_date') or '').strip()
|
||||
assignment.installer_id = installer_id or None
|
||||
if scheduled_date:
|
||||
assignment.scheduled_date = scheduled_date.replace('/', '-')
|
||||
assignment.assigned_by = request.user
|
||||
assignment.save()
|
||||
|
||||
# complete step
|
||||
StepInstance.objects.update_or_create(
|
||||
process_instance=instance,
|
||||
step=step,
|
||||
defaults={'status': 'completed', 'completed_at': timezone.now()}
|
||||
)
|
||||
|
||||
if next_step:
|
||||
instance.current_step = next_step
|
||||
instance.save()
|
||||
return redirect('processes:step_detail', instance_id=instance.id, step_id=next_step.id)
|
||||
return redirect('processes:request_list')
|
||||
|
||||
return render(request, 'installations/installation_assign_step.html', {
|
||||
'instance': instance,
|
||||
'step': step,
|
||||
'assignment': assignment,
|
||||
'installers': installers,
|
||||
'previous_step': previous_step,
|
||||
'next_step': next_step,
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
def installation_report_step(request, instance_id, step_id):
|
||||
instance = get_object_or_404(ProcessInstance, id=instance_id)
|
||||
step = get_object_or_404(instance.process.steps, id=step_id)
|
||||
previous_step = instance.process.steps.filter(order__lt=step.order).last()
|
||||
next_step = instance.process.steps.filter(order__gt=step.order).first()
|
||||
assignment = InstallationAssignment.objects.filter(process_instance=instance).first()
|
||||
existing_report = InstallationReport.objects.filter(assignment=assignment).order_by('-created').first()
|
||||
edit_mode = True if request.GET.get('edit') == '1' else False
|
||||
|
||||
# current quote items baseline
|
||||
quote = Quote.objects.filter(process_instance=instance).first()
|
||||
quote_items = list(quote.items.select_related('item').all()) if quote else []
|
||||
quote_price_map = {qi.item_id: qi.unit_price for qi in quote_items}
|
||||
items = Item.objects.all().order_by('name')
|
||||
|
||||
if request.method == 'POST':
|
||||
description = (request.POST.get('description') or '').strip()
|
||||
visited_date = (request.POST.get('visited_date') or '').strip()
|
||||
if '/' in visited_date:
|
||||
visited_date = visited_date.replace('/', '-')
|
||||
new_serial = (request.POST.get('new_water_meter_serial') or '').strip()
|
||||
seal_number = (request.POST.get('seal_number') or '').strip()
|
||||
is_suspicious = True if request.POST.get('is_meter_suspicious') == 'on' else False
|
||||
utm_x = request.POST.get('utm_x') or None
|
||||
utm_y = request.POST.get('utm_y') or None
|
||||
|
||||
# Build maps from form fields: remove and add
|
||||
remove_map = {}
|
||||
add_map = {}
|
||||
for key in request.POST.keys():
|
||||
if key.startswith('rem_') and key.endswith('_type'):
|
||||
# rem_{id}_type = 'remove'
|
||||
try:
|
||||
item_id = int(key.split('_')[1])
|
||||
except Exception:
|
||||
continue
|
||||
if request.POST.get(key) != 'remove':
|
||||
continue
|
||||
qty_val = request.POST.get(f'rem_{item_id}_qty') or '1'
|
||||
try:
|
||||
qty = int(qty_val)
|
||||
except Exception:
|
||||
qty = 1
|
||||
remove_map[item_id] = qty
|
||||
if key.startswith('add_') and key.endswith('_type'):
|
||||
try:
|
||||
item_id = int(key.split('_')[1])
|
||||
except Exception:
|
||||
continue
|
||||
if request.POST.get(key) != 'add':
|
||||
continue
|
||||
qty_val = request.POST.get(f'add_{item_id}_qty') or '1'
|
||||
price_val = request.POST.get(f'add_{item_id}_price')
|
||||
try:
|
||||
qty = int(qty_val)
|
||||
except Exception:
|
||||
qty = 1
|
||||
# resolve unit price
|
||||
unit_price = None
|
||||
if price_val:
|
||||
try:
|
||||
unit_price = Decimal(price_val)
|
||||
except InvalidOperation:
|
||||
unit_price = None
|
||||
if unit_price is None:
|
||||
item_obj = Item.objects.filter(id=item_id).first()
|
||||
unit_price = item_obj.unit_price if item_obj else None
|
||||
add_map[item_id] = {'qty': qty, 'price': unit_price}
|
||||
|
||||
# اجازهٔ ثبت همزمان حذف و افزودن برای یک قلم (بدون محدودیت و ادغام)
|
||||
|
||||
if existing_report and edit_mode:
|
||||
report = existing_report
|
||||
report.description = description
|
||||
report.visited_date = visited_date or None
|
||||
report.new_water_meter_serial = new_serial or None
|
||||
report.seal_number = seal_number or None
|
||||
report.is_meter_suspicious = is_suspicious
|
||||
report.utm_x = utm_x
|
||||
report.utm_y = utm_y
|
||||
report.save()
|
||||
# delete selected existing photos
|
||||
for key, val in request.POST.items():
|
||||
if key.startswith('del_photo_') and val == '1':
|
||||
try:
|
||||
pid = int(key.split('_')[-1])
|
||||
InstallationPhoto.objects.filter(id=pid, report=report).delete()
|
||||
except Exception:
|
||||
continue
|
||||
# append new photos
|
||||
for f in request.FILES.getlist('photos'):
|
||||
InstallationPhoto.objects.create(report=report, image=f)
|
||||
# replace item changes with new submission
|
||||
report.item_changes.all().delete()
|
||||
for item_id, qty in remove_map.items():
|
||||
up = quote_price_map.get(item_id)
|
||||
total = (up * qty) if up is not None else None
|
||||
InstallationItemChange.objects.create(
|
||||
report=report,
|
||||
item_id=item_id,
|
||||
change_type='remove',
|
||||
quantity=qty,
|
||||
unit_price=up,
|
||||
total_price=total,
|
||||
)
|
||||
for item_id, data in add_map.items():
|
||||
unit_price = data.get('price')
|
||||
qty = data.get('qty') or 1
|
||||
total = (unit_price * qty) if (unit_price is not None) else None
|
||||
InstallationItemChange.objects.create(
|
||||
report=report,
|
||||
item_id=item_id,
|
||||
change_type='add',
|
||||
quantity=qty,
|
||||
unit_price=unit_price,
|
||||
total_price=total,
|
||||
)
|
||||
else:
|
||||
report = InstallationReport.objects.create(
|
||||
assignment=assignment,
|
||||
description=description,
|
||||
visited_date=visited_date or None,
|
||||
new_water_meter_serial=new_serial or None,
|
||||
seal_number=seal_number or None,
|
||||
is_meter_suspicious=is_suspicious,
|
||||
utm_x=utm_x,
|
||||
utm_y=utm_y,
|
||||
created_by=request.user,
|
||||
)
|
||||
# photos
|
||||
for f in request.FILES.getlist('photos'):
|
||||
InstallationPhoto.objects.create(report=report, image=f)
|
||||
# item changes
|
||||
for item_id, qty in remove_map.items():
|
||||
up = quote_price_map.get(item_id)
|
||||
total = (up * qty) if up is not None else None
|
||||
InstallationItemChange.objects.create(
|
||||
report=report,
|
||||
item_id=item_id,
|
||||
change_type='remove',
|
||||
quantity=qty,
|
||||
unit_price=up,
|
||||
total_price=total,
|
||||
)
|
||||
for item_id, data in add_map.items():
|
||||
unit_price = data.get('price')
|
||||
qty = data.get('qty') or 1
|
||||
total = (unit_price * qty) if (unit_price is not None) else None
|
||||
InstallationItemChange.objects.create(
|
||||
report=report,
|
||||
item_id=item_id,
|
||||
change_type='add',
|
||||
quantity=qty,
|
||||
unit_price=unit_price,
|
||||
total_price=total,
|
||||
)
|
||||
|
||||
# complete step
|
||||
StepInstance.objects.update_or_create(
|
||||
process_instance=instance,
|
||||
step=step,
|
||||
defaults={'status': 'completed', 'completed_at': timezone.now()}
|
||||
)
|
||||
|
||||
if next_step:
|
||||
instance.current_step = next_step
|
||||
instance.save()
|
||||
return redirect('processes:step_detail', instance_id=instance.id, step_id=next_step.id)
|
||||
return redirect('processes:request_list')
|
||||
|
||||
# Build prefill maps from existing report changes
|
||||
removed_ids = set()
|
||||
removed_qty = {}
|
||||
added_map = {}
|
||||
if existing_report:
|
||||
for ch in existing_report.item_changes.all():
|
||||
if ch.change_type == 'remove':
|
||||
removed_ids.add(ch.item_id)
|
||||
removed_qty[ch.item_id] = ch.quantity
|
||||
elif ch.change_type == 'add':
|
||||
added_map[ch.item_id] = {'qty': ch.quantity, 'price': ch.unit_price}
|
||||
|
||||
return render(request, 'installations/installation_report_step.html', {
|
||||
'instance': instance,
|
||||
'step': step,
|
||||
'assignment': assignment,
|
||||
'report': existing_report,
|
||||
'edit_mode': edit_mode,
|
||||
'quote': quote,
|
||||
'quote_items': quote_items,
|
||||
'all_items': items,
|
||||
'removed_ids': removed_ids,
|
||||
'removed_qty': removed_qty,
|
||||
'added_map': added_map,
|
||||
'previous_step': previous_step,
|
||||
'next_step': next_step,
|
||||
})
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue