first commit
This commit is contained in:
@@ -8,8 +8,10 @@ class FormsClient(forms.ModelForm):
|
||||
'first_name',
|
||||
'last_name',
|
||||
'phone',
|
||||
'professional',
|
||||
'double_workload',
|
||||
'responsable',
|
||||
'professional',
|
||||
# 'late',
|
||||
# 'feed_back',
|
||||
# 'msg_3_months',
|
||||
# 'msg_6_months',
|
||||
@@ -21,8 +23,10 @@ class FormsClient(forms.ModelForm):
|
||||
'last_name': forms.TextInput({'class':'form-control'}),
|
||||
'notes': forms.Textarea({'class':'form-control','rows':3 , }),
|
||||
'phone': forms.NumberInput(attrs={'step': 1, }),
|
||||
'responsable': forms.Select({'class': 'form-control'}),
|
||||
'professional': forms.Select({'class':'form-control'}),
|
||||
'double_workload': forms.CheckboxInput({'class':''}),
|
||||
# 'late': forms.CheckboxInput({'class':''}),
|
||||
# 'msg_3_months': forms.CheckboxInput({'class':''}),
|
||||
# 'msg_6_months': forms.CheckboxInput({'class':''}),
|
||||
# 'msg_12_months': forms.CheckboxInput({'class':''}),
|
||||
@@ -32,8 +36,10 @@ class FormsClient(forms.ModelForm):
|
||||
'first_name':'Nome',
|
||||
'last_name':'Sobrenome',
|
||||
'phone':'Telefone',
|
||||
'responsable':'Responsavel',
|
||||
'professional':'Profissinal',
|
||||
'double_workload':'Tempo Duplo',
|
||||
# 'late':'Atrazildo',
|
||||
'notes':'Notas',
|
||||
# 'feed_back':'FeedBack',
|
||||
# 'msg_3_months':'FeedBack',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Generated by Django 5.2.7 on 2025-11-01 11:15
|
||||
# Generated by Django 6.0 on 2025-12-24 11:22
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
@@ -24,8 +24,9 @@ class Migration(migrations.Migration):
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('first_name', models.CharField(max_length=100)),
|
||||
('last_name', models.CharField(max_length=500)),
|
||||
('phone', models.DecimalField(decimal_places=0, max_digits=20)),
|
||||
('phone', models.DecimalField(decimal_places=0, max_digits=11)),
|
||||
('double_workload', models.BooleanField(default=False)),
|
||||
('last_visit', models.DateField(blank=True, null=True)),
|
||||
('feed_back', models.BooleanField(default=False)),
|
||||
('msg_3_months', models.BooleanField(default=False)),
|
||||
('msg_6_months', models.BooleanField(default=False)),
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# Generated by Django 5.2.8 on 2025-12-26 15:30
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Base', '0003_alter_chartofaccount_prof'),
|
||||
('Client', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='client',
|
||||
name='late',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='client',
|
||||
name='responsable',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='ClientResponsable', to='Client.client'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='client',
|
||||
name='professional',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='ClientProfessional', to='Base.professional'),
|
||||
),
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,20 +3,28 @@ from Base.models import Professional , Base
|
||||
|
||||
# Lista de clientes
|
||||
class Client(Base):
|
||||
first_name = models.CharField(max_length=100)
|
||||
last_name = models.CharField(max_length=500)
|
||||
phone = models.DecimalField(max_digits=20,decimal_places=0)
|
||||
first_name = models.CharField(max_length=100) # Nome
|
||||
last_name = models.CharField(max_length=500) # Sobrenome
|
||||
phone = models.DecimalField(max_digits=12,decimal_places=0) # Whatapp
|
||||
professional = models.ForeignKey(
|
||||
Professional,
|
||||
on_delete=models.PROTECT,
|
||||
on_delete=models.DO_NOTHING,
|
||||
related_name='ClientProfessional'
|
||||
)
|
||||
double_workload = models.BooleanField(default=False)
|
||||
feed_back = models.BooleanField(default=False)
|
||||
msg_3_months = models.BooleanField(default=False)
|
||||
msg_6_months = models.BooleanField(default=False)
|
||||
msg_12_months = models.BooleanField(default=False)
|
||||
notes = models.TextField(null=True, blank=True)
|
||||
) # profissinal do clinte
|
||||
responsable = models.ForeignKey(
|
||||
'self',
|
||||
on_delete=models.DO_NOTHING,
|
||||
related_name='ClientResponsable',
|
||||
null = True, blank = True
|
||||
) #responsalvewl do clinte
|
||||
double_workload = models.BooleanField(default=False) # Tempo dublo
|
||||
late = models.BooleanField(default=False) # costuma atrasar
|
||||
last_visit = models.DateField(null=True, blank=True) # ultimoa visita
|
||||
feed_back = models.BooleanField(default=False) # feedback no google
|
||||
msg_3_months = models.BooleanField(default=False) # mensagem de 3 messes que não vem
|
||||
msg_6_months = models.BooleanField(default=False) # mensagem de 6 messes que não vem
|
||||
msg_12_months = models.BooleanField(default=False) # mensagem de um ano que não vem
|
||||
notes = models.TextField(null=True, blank=True) # notas do clinte
|
||||
class Meta:
|
||||
ordering = ['first_name','last_name']
|
||||
def __str__(self):
|
||||
|
||||
@@ -2,23 +2,49 @@
|
||||
{% block title %} List {% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class="conteiner mt-4">
|
||||
<h3 class="display-6"> Cadastrar </h3>
|
||||
<div class="card col-md-10">
|
||||
<div class="container">
|
||||
<h3 class="mb-4">Cadastrar Cliente</h3>
|
||||
<div class="card shadow-sm col-md-8 ">
|
||||
<div class="card-body">
|
||||
<form method="post" class="form">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit" class="btn btn-primary"> Salvar </button>
|
||||
</form>
|
||||
<a href="{% url 'ClientListView' %}" class="btn btn-secondary mt-3 "> Cancelar e Voltar </a>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ form.first_name.label }}</label>
|
||||
{{ form.first_name }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ form.last_name.label }}</label>
|
||||
{{ form.last_name }}
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">{{ form.phone.label }}</label>
|
||||
{{ form.phone }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">{{ form.double_workload.label }}</label>
|
||||
{{ form.double_workload }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ form.professional.label }}</label>
|
||||
{{ form.professional }}
|
||||
</div>
|
||||
|
||||
<div class="mt-4 border-top pt-3 d-flex justify-content-between">
|
||||
<button type="submit" class="btn btn-primary px-4">Salvar Alterações</button>
|
||||
<button type="button" onclick="history.back()" class="btn btn-outline-secondary">Cancelar e Voltar</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
{% extends "BaseLogin.html" %}
|
||||
{% block title %} Bank Accounts List {% endblock %}
|
||||
{% block content %}
|
||||
<div class="conteiner mt-4">
|
||||
<h3 class="display-6"> Detalhes do Banco </h3>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">{{ object.first_name }} {{ object.last_name }}</h3><br>
|
||||
<p class="card-text">Tel.: {{ object.phone }}</p>
|
||||
<p class="card-text">Prof. de Preferencia : {{ object.professional }}</p>
|
||||
<p class="card-text">Tempo Duplo : {{ object.double_workload }}</p>
|
||||
<p class="card-text">Nota: {{ object.notes }}</p>
|
||||
<br>
|
||||
|
||||
<h3> Ultimas visitas </h3>
|
||||
<div class="col-4">
|
||||
<table class="table table-striped table-sm table-hover ">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Dia </th>
|
||||
<th>Horario</th>
|
||||
<th>Profissinal</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="table table-bordered">
|
||||
{% for client in clients %}
|
||||
<tr>
|
||||
<td>{{ client.date|date:"d/m/Y" }}</td>
|
||||
<td>{{ client.time }} </td>
|
||||
<td>{{ client.professional }} </td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="2"> Nada </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<a href="{% url 'ClientUpdateView' object.id %}" class="btn btn-secondary mt-3 "> Editar </a>
|
||||
</div>
|
||||
</div>
|
||||
<a href="{% url 'ClientListView' %}" class="btn btn-secondary mt-3 "> Cancelar e Voltar </a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
26
Client/templates/Client/Detail/Detail.html
Normal file
26
Client/templates/Client/Detail/Detail.html
Normal file
@@ -0,0 +1,26 @@
|
||||
{% extends "BaseLogin.html" %}
|
||||
{% block title %} Cliente {% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class="container mt-4">
|
||||
{# <h3 class="display-6 mb-4">Detalhes do Cliente</h3>#}
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
{% include 'Client/Detail/Detail_p1.html' %}
|
||||
<div class="col-md-6 ps-md-4">
|
||||
{% include 'Client/Detail/Detail_p2.html' %}
|
||||
{% include 'Client/Detail/Detail_p3.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
{% include 'Client/Detail/Detail_btn.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
32
Client/templates/Client/Detail/Detail_btn.html
Normal file
32
Client/templates/Client/Detail/Detail_btn.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<div class="d-flex justify-content-between align-items-center mt-4">
|
||||
|
||||
<form method="get" action="{% url 'MovCalendarCreateCustom' %}">
|
||||
<input type="hidden" name="Client" value="{{ object.id }}">
|
||||
<button type="submit" class="btn btn-primary">Marcar Atendimento </button>
|
||||
</form>
|
||||
|
||||
<form method="get" action="{% url 'MovProductCreateCustom' %}">
|
||||
<input type="hidden" name="Client" value="{{ object.id }}">
|
||||
<button type="submit" class="btn btn-success">Produto</button>
|
||||
</form>
|
||||
|
||||
{% if perms.brands.change_brands %}
|
||||
<a href="{% url 'ClientUpdateView' object.id %}" class="btn btn-warning btn-sm">
|
||||
Editar Infirmações
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if perms.brands.change_brands %}
|
||||
<a href="{% url 'ClientUpdateResponsableView' object.id %}" class="btn btn-warning btn-sm">
|
||||
Editar Responsavel
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
<button type="button" onclick="history.back()" class="btn btn-danger">
|
||||
<i class="bi bi-arrow-left"></i> Cancelar e Voltar
|
||||
</button>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
21
Client/templates/Client/Detail/Detail_p1.html
Normal file
21
Client/templates/Client/Detail/Detail_p1.html
Normal file
@@ -0,0 +1,21 @@
|
||||
{% load phone_filters %}
|
||||
<div class="col-md-6 border-end">
|
||||
<h1 class="card-title text-primary">{{ object.first_name }} {{ object.last_name }}</h1>
|
||||
<hr>
|
||||
<p class="card-text"><strong>Tel.:</strong> {{ object.phone|format_phone_true }}</p>
|
||||
<p class="card-text"><strong>Prof. de Preferência:</strong> {{ object.professional }}</p>
|
||||
{% if object.responsable %}
|
||||
<p class="card-text d-flex align-items-center">
|
||||
<i class="bi bi-person-badge text-info me-2"></i>
|
||||
<strong>Responsável:</strong>
|
||||
<a href="{% url 'ClientDetailView' object.responsable.id %}"
|
||||
class="btn btn-link p-0 ms-1 text-decoration-none">
|
||||
{{ object.responsable }}
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
<p class="card-text"><strong>Última visita:</strong> {{ object.last_visit|date:"d/m/Y" }}</p>
|
||||
<p class="card-text"><strong>Nota:</strong> {{ object.notes|default:"Nenhuma nota adicionada." }}</p>
|
||||
</div>
|
||||
|
||||
|
||||
46
Client/templates/Client/Detail/Detail_p2.html
Normal file
46
Client/templates/Client/Detail/Detail_p2.html
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
<h5 class="text-muted mb-3">Detalhes</h5>
|
||||
<p class="card-text">
|
||||
<form method="post" class="m-0">
|
||||
{% csrf_token %}
|
||||
<button type="submit" name="double_workload" value="double_workload" class="btn p-0">
|
||||
{% if object.double_workload %}
|
||||
<i class="bi bi-check-circle-fill text-success me-2"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-x-circle-fill text-danger me-2"></i>
|
||||
{% endif %}
|
||||
</button>
|
||||
Tempo Duplo
|
||||
</form>
|
||||
</p>
|
||||
|
||||
<p class="card-text">
|
||||
<form method="post" class="m-0">
|
||||
{% csrf_token %}
|
||||
<button type="submit" name="late" value="late" class="btn p-0">
|
||||
{% if object.late %}
|
||||
<i class="bi bi-check-circle-fill text-success me-2"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-x-circle-fill text-danger me-2"></i>
|
||||
{% endif %}
|
||||
</button>
|
||||
Costuma Atrasar
|
||||
</form>
|
||||
</p>
|
||||
|
||||
<p class="card-text">
|
||||
<form method="post" class="m-0">
|
||||
{% csrf_token %}
|
||||
<button type="submit" name="feed_back" value="feed_back" class="btn p-0">
|
||||
{% if object.feed_back %}
|
||||
<i class="bi bi-check-circle-fill text-success me-2"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-x-circle-fill text-danger me-2"></i>
|
||||
{% endif %}
|
||||
</button>
|
||||
FeedBack no Google
|
||||
</form>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
43
Client/templates/Client/Detail/Detail_p3.html
Normal file
43
Client/templates/Client/Detail/Detail_p3.html
Normal file
@@ -0,0 +1,43 @@
|
||||
<h5 class="text-muted mb-3">Lembrete de volta</h5>
|
||||
|
||||
<div class="d-flex gap-3">
|
||||
<form method="post" class="m-0">
|
||||
{% csrf_token %}
|
||||
<button type="submit" name="msg_3_months" value="msg_3_months" class="btn p-0">
|
||||
{% if object.msg_3_months %}
|
||||
<i class="bi bi-check-circle-fill text-success me-2"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-x-circle-fill text-danger me-2"></i>
|
||||
{% endif %}
|
||||
</button>
|
||||
3 meses
|
||||
</form>
|
||||
|
||||
<form method="post" class="m-0">
|
||||
{% csrf_token %}
|
||||
<button type="submit" name="msg_6_months" value="msg_6_months" class="btn p-0">
|
||||
{% if object.msg_6_months %}
|
||||
<i class="bi bi-check-circle-fill text-success me-2"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-x-circle-fill text-danger me-2"></i>
|
||||
{% endif %}
|
||||
</button>
|
||||
6 meses
|
||||
</form>
|
||||
|
||||
<form method="post" class="m-0">
|
||||
{% csrf_token %}
|
||||
<button type="submit" name="msg_12_months" value="msg_12_months" class="btn p-0">
|
||||
{% if object.msg_12_months %}
|
||||
<i class="bi bi-check-circle-fill text-success me-2"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-x-circle-fill text-danger me-2"></i>
|
||||
{% endif %}
|
||||
</button>
|
||||
1 Ano
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{% extends "BaseLogin.html" %}
|
||||
{% block title %} List {% endblock %}
|
||||
{% load phone_filters %}
|
||||
{% block content %}
|
||||
<h1> Clientes </h1><br>
|
||||
<div class="row mb-3">
|
||||
@@ -29,83 +30,46 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{# <div class="col-md-3">#}
|
||||
{# <form method="get" action="{% url 'ClientListView' %}" id="filterForm3">#}
|
||||
{# <div class="input-group">#}
|
||||
{# <input type="hidden" class="form-control" name="all"#}
|
||||
{# placeholder="" value="all"#}
|
||||
{# onchange="document.getElementById('filterForm3').submit();"#}
|
||||
{# >#}
|
||||
{# <button type="submit" class="btn btn-primary"> Tudo#}
|
||||
{# <i class="bi bi-search"></i>#}
|
||||
{# </button>#}
|
||||
{# </div>#}
|
||||
{# </form>#}
|
||||
{# </div>#}
|
||||
{# {% if perms.brands.add_brands %}#}
|
||||
<div class="col-md-1">
|
||||
<a href="{% url 'ClientCreateView' %}" class="btn btn-success float-end">
|
||||
{# <i class="bi bi-plus"></i> #}
|
||||
Criar </a>
|
||||
</div>
|
||||
{# {% endif %}#}
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead class="thead-dark">
|
||||
<tr class="text-center">
|
||||
<th width="5%"> Marcar </th>
|
||||
<th> Nome </th>
|
||||
<th> Sobrenome </th>
|
||||
<th width="10%"> Telefone </th>
|
||||
<th width="5%"> </th>
|
||||
<th width="5%"> </th>
|
||||
<th> Nome Completo </th>
|
||||
<th width="20%"> Telefone </th>
|
||||
<th width="10%"> Profissinal </th>
|
||||
<th width="08%"> T. D. </th>
|
||||
<th width="05%"> FeedBack </th>
|
||||
<th width="15%"> Notas </th>
|
||||
<th width="5%"> Produto </th>
|
||||
<th width="15%"> Ações </th>
|
||||
<th width="08%"> T. Duplo </th>
|
||||
<th width="15%"> U. Visita </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody align="center">
|
||||
{% for Client in Clients %}
|
||||
<tr align="center" vertical-align="center">
|
||||
<td>
|
||||
<form method="get" action="{% url 'MovCalendarCreateCustom' %}" >
|
||||
<a href="{% url 'ClientDetailView' Client.id %}" class="">
|
||||
{# <i class="bi bi-eye"></i>#}
|
||||
<button type="submit" class="btn btn-primary"> Detalhes </button>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<form method="get" action="{% url 'MovCalendarCreateCustom' %}">
|
||||
<input type="hidden" name="Client" value="{{ Client.id }}">
|
||||
<button type="submit" class="btn btn-primary"> Marcar </button>
|
||||
<button type="submit" class="btn btn-primary">Marcar </button>
|
||||
</form>
|
||||
</td>
|
||||
<td> {{ Client.first_name }} </td>
|
||||
<td> {{ Client.last_name }} </td>
|
||||
<td> {{ Client.phone }} </td>
|
||||
<td> {{ Client.professional.symbol }} {{ Client.professional }}</td>
|
||||
<td> {{ Client.first_name }} {{ Client.last_name }} {{ Client.professional.symbol }}</td>
|
||||
<td> {{ Client.phone|format_phone }} </td>
|
||||
<td> {{ Client.professional.name }}</td>
|
||||
<td> {% if Client.double_workload %} <i class="bi bi-check-circle-fill text-success "></i>
|
||||
{% else %} <i class="bi bi-x-circle-fill text-danger "></i> {% endif %} </td>
|
||||
<td> {% if Client.feed_back %} <i class="bi bi-check-circle-fill text-success "></i>
|
||||
{% else %} <i class="bi bi-x-circle-fill text-danger "></i> {% endif %} </td>
|
||||
<td> {{ Client.notes }} </td>
|
||||
<td>
|
||||
<form method="get" action="{% url 'MovProductCreateCustom' %}" >
|
||||
<input type="hidden" name="Client" value="{{ Client.id }}">
|
||||
<button type="submit" class="btn btn-primary"> Produto </button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{% url 'ClientDetailView' Client.id %}" class="btn btn-info btn-sm">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
{# {% if perms.brands.change_brands %}#}
|
||||
<a href="{% url 'ClientUpdateView' Client.id %}" class="btn btn-warning btn-sm">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
{# {% endif %}#}
|
||||
{# {% if perms.brands.delete_brands %}#}
|
||||
<a href="{% url 'ClientDeleteView' Client.id %}" class="btn btn-danger btn-sm">
|
||||
<i class="bi bi-trash"></i>
|
||||
</a>
|
||||
{# {% endif %}#}
|
||||
</td>
|
||||
<td> {{ Client.last_visit|date:"d/m/Y" }} </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@@ -2,15 +2,62 @@
|
||||
{% block title %} Bank Accounts List {% endblock %}
|
||||
{% block content %}
|
||||
<div class="conteiner mt-4">
|
||||
<h3 class="display-6"> Editar Bancos </h3>
|
||||
<h3 class="display-6"> Editar Clientes </h3>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="post" class="form">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit" class="btn btn-primary"> Salvar </button>
|
||||
</form>
|
||||
<a href="{% url 'ClientListView' %}" class="btn btn-secondary mt-3 "> Cancelar e Voltar </a>
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="{{ form.first_name.id_for_label }}" class="form-label">Nome</label>
|
||||
<input type="text" name="{{ form.first_name.name }}" id="{{ form.first_name.id_for_label }}"
|
||||
class="form-control" value="{{ form.first_name.value|default:'' }}">
|
||||
{% if form.first_name.errors %}<div class="text-danger small">{{ form.first_name.errors }}</div>{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="{{ form.last_name.id_for_label }}" class="form-label">Sobrenome</label>
|
||||
<input type="text" name="{{ form.last_name.name }}" id="{{ form.last_name.id_for_label }}"
|
||||
class="form-control" value="{{ form.last_name.value|default:'' }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="{{ form.phone.id_for_label }}" class="form-label">WhatsApp (Somente números)</label>
|
||||
<input type="number" name="{{ form.phone.name }}" id="{{ form.phone.id_for_label }}"
|
||||
class="form-control" value="{{ form.phone.value|default:'' }}">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="{{ form.professional.id_for_label }}" class="form-label">Profissional</label>
|
||||
<select name="{{ form.professional.name }}" id="{{ form.professional.id_for_label }}" class="form-select">
|
||||
{% for choice in form.professional.field.choices %}
|
||||
<option value="{{ choice.0 }}" {% if form.professional.value|stringformat:"s" == choice.0|stringformat:"s" %}selected{% endif %}>
|
||||
{{ choice.1 }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="double_workload" id="id_double_workload" {% if form.double_workload.value %}checked{% endif %}>
|
||||
<label class="form-check-label" for="id_double_workload">Tempo Duplo</label>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.notes.id_for_label }}" class="form-label">Notas do Cliente</label>
|
||||
<textarea name="{{ form.notes.name }}" id="{{ form.notes.id_for_label }}" class="form-control" rows="3">{{ form.notes.value|default:'' }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 border-top pt-3 d-flex justify-content-between">
|
||||
<button type="submit" class="btn btn-primary px-4">Salvar Alterações</button>
|
||||
<button type="button" onclick="history.back()" class="btn btn-outline-secondary">Cancelar e Voltar</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
72
Client/templates/Client/Update_responsable.html
Normal file
72
Client/templates/Client/Update_responsable.html
Normal file
@@ -0,0 +1,72 @@
|
||||
{% extends "BaseLogin.html" %}
|
||||
{% load phone_filters %}
|
||||
|
||||
{% block title %} Editar Cliente {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-4">
|
||||
<h3 class="display-6 mb-4">Editar Responsável</h3>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
|
||||
<form method="GET" action="{% url 'ClientUpdateResponsableView' pk=object.pk %}" class="mb-4">
|
||||
<label for="filterClient" class="form-label font-weight-bold">Filtrar Cliente</label>
|
||||
<div class="input-group">
|
||||
<input type="text"
|
||||
id="filterClient"
|
||||
name="Client"
|
||||
placeholder="Digite o nome para buscar..."
|
||||
class="form-control"
|
||||
value="{{ request.GET.Client }}">
|
||||
<button type="submit" class="btn btn-primary">Filtrar</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<input type="hidden" name="first_name" value="{{ object.first_name }}">
|
||||
<input type="hidden" name="last_name" value="{{ object.last_name }}">
|
||||
<input type="hidden" name="phone" value="{{ object.phone }}">
|
||||
<input type="hidden" name="professional" value="{{ object.professional.id }}">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
<label class="form-label"><strong>Selecione o Responsável</strong></label>
|
||||
|
||||
<div class="list-group">
|
||||
{% for client in Clients %}
|
||||
<label class="list-group-item list-group-item-action d-flex align-items-center">
|
||||
<input type="radio"
|
||||
name="responsable"
|
||||
value="{{ client.id }}"
|
||||
class="form-check-input me-3"
|
||||
id="client_{{ client.id }}"
|
||||
{% if object.responsable.id == client.id %}checked{% endif %}>
|
||||
<span>
|
||||
{{ client.first_name }} {{ client.last_name }}
|
||||
<small class="text-muted d-block d-md-inline ms-md-2">
|
||||
{{ client.phone|format_phone }}
|
||||
</small>
|
||||
</span>
|
||||
</label>
|
||||
{% empty %}
|
||||
<div class="alert alert-light border">Nenhum cliente encontrado.</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 border-top pt-3 d-flex justify-content-between">
|
||||
<button type="submit" class="btn btn-primary px-4">Salvar Alterações</button>
|
||||
<button type="button" onclick="history.back()" class="btn btn-outline-secondary">Cancelar e Voltar</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
0
Client/templatetags/__init__.py
Normal file
0
Client/templatetags/__init__.py
Normal file
BIN
Client/templatetags/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
Client/templatetags/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
Client/templatetags/__pycache__/phone_filters.cpython-312.pyc
Normal file
BIN
Client/templatetags/__pycache__/phone_filters.cpython-312.pyc
Normal file
Binary file not shown.
36
Client/templatetags/phone_filters.py
Normal file
36
Client/templatetags/phone_filters.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from django import template
|
||||
register = template.Library()
|
||||
|
||||
@register.filter
|
||||
def format_phone(value):
|
||||
"""Formata telefone ocultando os números centrais."""
|
||||
if not value:
|
||||
return ""
|
||||
|
||||
# Remove qualquer caractere não numérico
|
||||
value = "".join(filter(str.isdigit, str(value)))
|
||||
|
||||
if len(value) == 11: # Celular: (XX) X****-XXXX
|
||||
# Mantém o DDD, o primeiro dígito do número e os 4 finais
|
||||
return f"({value[0:2]}) {value[2]}****-{value[7:]}"
|
||||
|
||||
elif len(value) == 10: # Fixo: (XX) ****-XXXX
|
||||
# Mantém o DDD e os 4 finais, oculta os 4 iniciais do número
|
||||
return f"({value[0:2]}) ****-{value[6:]}"
|
||||
|
||||
else:
|
||||
return value
|
||||
@register.filter
|
||||
def format_phone_true(value):
|
||||
"""Formata telefone no padrão (XX) XXXXX-XXXX."""
|
||||
if not value:
|
||||
return ""
|
||||
|
||||
value = "".join(filter(str.isdigit, str(value))) # remove qualquer caractere não numérico
|
||||
|
||||
if len(value) == 11: # Ex: 21998139861
|
||||
return f"({value[0:2]}) {value[2:7]}-{value[7:]}"
|
||||
elif len(value) == 10: # Ex: 2134567890
|
||||
return f"({value[0:2]}) {value[2:6]}-{value[6:]}"
|
||||
else:
|
||||
return value # caso não caiba nos padrões
|
||||
@@ -3,10 +3,11 @@ from Client import viewsClient
|
||||
|
||||
urlpatterns = [
|
||||
# URLs de Client
|
||||
path('/', viewsClient.ClientListView.as_view(), name='ClientListView'),
|
||||
path('/Create', viewsClient.ClientCreateView.as_view(), name='ClientCreateView'),
|
||||
path('/<int:pk>/Detail', viewsClient.ClientDetailView.as_view(), name='ClientDetailView'),
|
||||
path('/<int:pk>/Update', viewsClient.ClientUpdateView.as_view(), name='ClientUpdateView'),
|
||||
path('/<int:pk>/Delete', viewsClient.ClientDeleteView.as_view(), name='ClientDeleteView'),
|
||||
path('', viewsClient.ClientListView.as_view(), name='ClientListView'),
|
||||
path('Create', viewsClient.ClientCreateView.as_view(), name='ClientCreateView'),
|
||||
path('<int:pk>/Detail', viewsClient.ClientDetailView.as_view(), name='ClientDetailView'),
|
||||
path('<int:pk>/Update', viewsClient.ClientUpdateView.as_view(), name='ClientUpdateView'),
|
||||
path('<int:pk>/UpdateResp', viewsClient.ClientUpdateResponsableView.as_view(), name='ClientUpdateResponsableView'),
|
||||
path('<int:pk>/Delete', viewsClient.ClientDeleteView.as_view(), name='ClientDeleteView'),
|
||||
|
||||
]
|
||||
@@ -4,6 +4,10 @@ from Client import models, FormsClient
|
||||
from Movement.models import Calendar
|
||||
from django.urls import reverse_lazy
|
||||
# from django.core import serializers
|
||||
from django.shortcuts import redirect
|
||||
from django.contrib import messages
|
||||
# importate para função ou no filtro
|
||||
from django.db.models import Q
|
||||
|
||||
class ClientListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||||
model = models.Client
|
||||
@@ -26,7 +30,6 @@ class ClientListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||||
queryset = queryset.none()
|
||||
return queryset
|
||||
|
||||
|
||||
class ClientCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||||
model = models.Client
|
||||
template_name = 'Client/Create.html'
|
||||
@@ -34,18 +37,33 @@ class ClientCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||||
success_url = reverse_lazy('ClientListView')
|
||||
permission_required = 'Client.add_client'
|
||||
|
||||
|
||||
class ClientDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
|
||||
model = models.Client
|
||||
template_name = 'Client/Detail.html'
|
||||
template_name = 'Client/Detail/Detail.html'
|
||||
permission_required = 'Client.view_client'
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['clients'] = Calendar.objects.filter(
|
||||
client__id=self.object.id
|
||||
).order_by('-id').distinct()[:3]
|
||||
return context
|
||||
if request.POST.get('double_workload') == 'double_workload':
|
||||
self.object.double_workload = not self.object.double_workload
|
||||
|
||||
if request.POST.get('late') == 'late':
|
||||
self.object.late = not self.object.late
|
||||
|
||||
if request.POST.get('feed_back') == 'feed_back':
|
||||
self.object.feed_back = not self.object.feed_back
|
||||
|
||||
if request.POST.get('msg_3_months') == 'msg_3_months':
|
||||
self.object.msg_3_months = not self.object.msg_3_months
|
||||
|
||||
if request.POST.get('msg_6_months') == 'msg_6_months':
|
||||
self.object.msg_6_months = not self.object.msg_6_months
|
||||
|
||||
if request.POST.get('msg_12_months') == 'msg_12_months':
|
||||
self.object.msg_12_months = not self.object.msg_12_months
|
||||
|
||||
self.object.save()
|
||||
return redirect('ClientDetailView', pk=self.object.id)
|
||||
|
||||
class ClientUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||||
model = models.Client
|
||||
@@ -54,6 +72,42 @@ class ClientUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||||
success_url = reverse_lazy('ClientListView')
|
||||
permission_required = 'Client.change_client'
|
||||
|
||||
class ClientUpdateResponsableView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||||
model = models.Client
|
||||
template_name = 'Client/Update_responsable.html'
|
||||
form_class = FormsClient.FormsClient
|
||||
permission_required = 'Client.change_client'
|
||||
|
||||
def get_success_url(self):
|
||||
return redirect('ClientDetailView', pk=self.object.pk)['Location']
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
search_term = self.request.GET.get('Client')
|
||||
|
||||
if search_term:
|
||||
filter_client = models.Client.objects.filter(
|
||||
Q(first_name__icontains=search_term) |
|
||||
Q(last_name__icontains=search_term)
|
||||
)
|
||||
else:
|
||||
filter_client = models.Client.objects.none()
|
||||
|
||||
context['Clients'] = filter_client
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
print(self.request)
|
||||
form.instance.created_by = self.request.user
|
||||
messages.success(self.request, 'Registro realizado com sucesso!')
|
||||
return super().form_valid(form)
|
||||
|
||||
def form_invalid(self, form):
|
||||
print(self.request)
|
||||
errors = form.errors.as_text()
|
||||
messages.error(self.request, f'Verifique os dados: {errors}')
|
||||
return super().form_invalid(form)
|
||||
|
||||
class ClientDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
||||
model = models.Client
|
||||
template_name = 'Client/Delete.html'
|
||||
|
||||
Reference in New Issue
Block a user