adicionado criacao de OSs Oriontard
This commit is contained in:
parent
56941e2978
commit
445b7b5812
|
|
@ -124,3 +124,31 @@ global.ExecuteQueryAgro = function ExecuteQueryAgro(sqlQuery, req, res, params =
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
//#region ORIONTARD
|
||||||
|
var ConfigMySQLOriontard = {
|
||||||
|
connectionLimit : 10,
|
||||||
|
host : 'zendioninc.com.br',
|
||||||
|
port : '3306',
|
||||||
|
user : 'zendion',
|
||||||
|
password : 'Z210203!',
|
||||||
|
database : 'oriontard',
|
||||||
|
debug : false,
|
||||||
|
multipleStatements : true
|
||||||
|
};
|
||||||
|
|
||||||
|
global.AbreConexaoOriontardMySQL = function AbreConexaoOriontardMySQL() {
|
||||||
|
global.ConexaoMySQL_Oriontard = mysql.createPool(ConfigMySQLOriontard);
|
||||||
|
console.log("MySQL Conectado! Oriontard");
|
||||||
|
}
|
||||||
|
AbreConexaoOriontardMySQL();
|
||||||
|
|
||||||
|
global.ExecuteQueryOriontard = function ExecuteQueryOriontard(sqlQuery, req, res, params = [], callback = null) {
|
||||||
|
ConexaoMySQL_Oriontard.query(sqlQuery, params, function (error, results, fields) {
|
||||||
|
console.log(`${new Date().toUTCString()} - ${req.originalUrl} ${(error) ? ' 500 error' : ' 200 success'}`);
|
||||||
|
res.send(JSON.stringify({ "status": ((error) ? 500 : 200), "error": error, "response": results }))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ var routes = function () {
|
||||||
const produto = results[0];
|
const produto = results[0];
|
||||||
if (produto['idtiposlicenca'] == 6) {
|
if (produto['idtiposlicenca'] == 6) {
|
||||||
const sqlGerarChave = `CALL GerarInserirChave(?, ?, ?, @SerialKey, @idChave);`;
|
const sqlGerarChave = `CALL GerarInserirChave(?, ?, ?, @SerialKey, @idChave);`;
|
||||||
mysqlConnection.query(sqlGerarChave, [produto['cpf'], 30, produto['idsistemas']], function (chaveError, resChave) {
|
mysqlConnection.query(sqlGerarChave, [produto['cpf'], 90, produto['idsistemas']], function (chaveError, resChave) {
|
||||||
if (chaveError) {
|
if (chaveError) {
|
||||||
return res.status(500).send({ error: 'Erro ao gerar chave.' });
|
return res.status(500).send({ error: 'Erro ao gerar chave.' });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,424 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../connector');
|
||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
const {
|
||||||
|
gerarOrdemServicoPdf,
|
||||||
|
gerarRelatorioOrdensServicoPdf
|
||||||
|
} = require('../services/orionPdfService');
|
||||||
|
|
||||||
|
function getPool() {
|
||||||
|
if (!global.ConexaoMySQL_Oriontard) {
|
||||||
|
throw new Error('Pool do banco Oriontard não foi inicializado.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return global.ConexaoMySQL_Oriontard;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dbQuery(sql, params = []) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
getPool().query(sql, params, (error, results, fields) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve({ results, fields });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function positiveInteger(value, fieldName) {
|
||||||
|
const number = Number(value);
|
||||||
|
|
||||||
|
if (!Number.isInteger(number) || number <= 0) {
|
||||||
|
const error = new Error(`${fieldName} inválido.`);
|
||||||
|
error.httpStatus = 400;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendError(req, res, error) {
|
||||||
|
const status = error.httpStatus || 500;
|
||||||
|
const message = status >= 500
|
||||||
|
? 'Erro interno ao gerar o relatório.'
|
||||||
|
: error.message;
|
||||||
|
|
||||||
|
console.error(
|
||||||
|
`${new Date().toUTCString()} - ${req.originalUrl} ${status} error`,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.headersSent) {
|
||||||
|
res.destroy(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(status).json({
|
||||||
|
status,
|
||||||
|
error: {
|
||||||
|
code: error.code || null,
|
||||||
|
message
|
||||||
|
},
|
||||||
|
response: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function dateToSql(date) {
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
|
||||||
|
return `${year}-${month}-${day}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseDateOrDefault(value, fallback) {
|
||||||
|
if (!value) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
const date = new Date(`${value}T00:00:00`);
|
||||||
|
|
||||||
|
if (Number.isNaN(date.getTime())) {
|
||||||
|
const error = new Error('Data de filtro inválida.');
|
||||||
|
error.httpStatus = 400;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadFullOrder(id) {
|
||||||
|
const [
|
||||||
|
{ results: orders },
|
||||||
|
{ results: activities },
|
||||||
|
{ results: technicians },
|
||||||
|
{ results: materials },
|
||||||
|
{ results: odometers },
|
||||||
|
{ results: payments }
|
||||||
|
] = await Promise.all([
|
||||||
|
dbQuery(
|
||||||
|
`
|
||||||
|
SELECT
|
||||||
|
os.*,
|
||||||
|
t.nome AS tecnico_responsavel_nome,
|
||||||
|
GREATEST(os.valor_total - os.valor_pago, 0) AS valor_pendente
|
||||||
|
FROM manut_ordens_servico os
|
||||||
|
LEFT JOIN manut_tecnicos t
|
||||||
|
ON t.id = os.tecnico_responsavel_id
|
||||||
|
WHERE os.id = ?
|
||||||
|
AND os.excluido_em IS NULL
|
||||||
|
LIMIT 1;
|
||||||
|
`,
|
||||||
|
[id]
|
||||||
|
),
|
||||||
|
dbQuery(
|
||||||
|
`
|
||||||
|
SELECT *
|
||||||
|
FROM manut_ordem_servico_atividades
|
||||||
|
WHERE ordem_servico_id = ?
|
||||||
|
ORDER BY data_atividade, ordem_exibicao, id;
|
||||||
|
`,
|
||||||
|
[id]
|
||||||
|
),
|
||||||
|
dbQuery(
|
||||||
|
`
|
||||||
|
SELECT
|
||||||
|
at.atividade_id,
|
||||||
|
at.tecnico_id,
|
||||||
|
at.papel,
|
||||||
|
at.horas_trabalhadas_expediente,
|
||||||
|
at.horas_trabalhadas_extras,
|
||||||
|
t.nome AS tecnico_nome
|
||||||
|
FROM manut_ordem_servico_atividade_tecnicos at
|
||||||
|
INNER JOIN manut_tecnicos t
|
||||||
|
ON t.id = at.tecnico_id
|
||||||
|
INNER JOIN manut_ordem_servico_atividades a
|
||||||
|
ON a.id = at.atividade_id
|
||||||
|
WHERE a.ordem_servico_id = ?
|
||||||
|
ORDER BY t.nome;
|
||||||
|
`,
|
||||||
|
[id]
|
||||||
|
),
|
||||||
|
dbQuery(
|
||||||
|
`
|
||||||
|
SELECT mat.*
|
||||||
|
FROM manut_ordem_servico_atividade_materiais mat
|
||||||
|
INNER JOIN manut_ordem_servico_atividades a
|
||||||
|
ON a.id = mat.atividade_id
|
||||||
|
WHERE a.ordem_servico_id = ?
|
||||||
|
ORDER BY mat.id;
|
||||||
|
`,
|
||||||
|
[id]
|
||||||
|
),
|
||||||
|
dbQuery(
|
||||||
|
`
|
||||||
|
SELECT *
|
||||||
|
FROM manut_equipamento_odometro_leituras
|
||||||
|
WHERE ordem_servico_id = ?
|
||||||
|
ORDER BY leitura_em, id;
|
||||||
|
`,
|
||||||
|
[id]
|
||||||
|
),
|
||||||
|
dbQuery(
|
||||||
|
`
|
||||||
|
SELECT *
|
||||||
|
FROM manut_ordem_servico_pagamentos
|
||||||
|
WHERE ordem_servico_id = ?
|
||||||
|
AND cancelado_em IS NULL
|
||||||
|
ORDER BY data_pagamento, id;
|
||||||
|
`,
|
||||||
|
[id]
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!orders.length) {
|
||||||
|
const error = new Error('Ordem de serviço não encontrada.');
|
||||||
|
error.httpStatus = 404;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const techniciansByActivity = new Map();
|
||||||
|
technicians.forEach(item => {
|
||||||
|
if (!techniciansByActivity.has(item.atividade_id)) {
|
||||||
|
techniciansByActivity.set(item.atividade_id, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
techniciansByActivity.get(item.atividade_id).push(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
const materialsByActivity = new Map();
|
||||||
|
materials.forEach(item => {
|
||||||
|
if (!materialsByActivity.has(item.atividade_id)) {
|
||||||
|
materialsByActivity.set(item.atividade_id, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
materialsByActivity.get(item.atividade_id).push(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
const nestedActivities = activities.map(activity => ({
|
||||||
|
...activity,
|
||||||
|
tecnicos: techniciansByActivity.get(activity.id) || [],
|
||||||
|
materiais: materialsByActivity.get(activity.id) || []
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
...orders[0],
|
||||||
|
atividades: nestedActivities,
|
||||||
|
odometros_da_os: odometers,
|
||||||
|
pagamentos: payments
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildReportFilters(query) {
|
||||||
|
const now = new Date();
|
||||||
|
const firstDay = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||||
|
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0);
|
||||||
|
|
||||||
|
const dataInicio = parseDateOrDefault(query.data_inicio, firstDay);
|
||||||
|
const dataFim = parseDateOrDefault(query.data_fim, lastDay);
|
||||||
|
|
||||||
|
if (dataFim < dataInicio) {
|
||||||
|
const error = new Error('A data final não pode ser anterior à data inicial.');
|
||||||
|
error.httpStatus = 400;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const criterios = {
|
||||||
|
data_entrada: {
|
||||||
|
coluna: 'os.data_entrada',
|
||||||
|
label: 'data de entrada'
|
||||||
|
},
|
||||||
|
data_finalizacao: {
|
||||||
|
coluna: 'os.data_finalizacao',
|
||||||
|
label: 'data de finalização'
|
||||||
|
},
|
||||||
|
data_retirada: {
|
||||||
|
coluna: 'os.data_retirada',
|
||||||
|
label: 'data de retirada'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const criterio = criterios[query.criterio] || criterios.data_finalizacao;
|
||||||
|
|
||||||
|
return {
|
||||||
|
dataInicio,
|
||||||
|
dataFim,
|
||||||
|
dataInicioSql: dateToSql(dataInicio),
|
||||||
|
dataFimSql: dateToSql(dataFim),
|
||||||
|
criterio,
|
||||||
|
clienteId: query.cliente_id
|
||||||
|
? positiveInteger(query.cliente_id, 'Cliente')
|
||||||
|
: null,
|
||||||
|
equipamentoId: query.equipamento_id
|
||||||
|
? positiveInteger(query.equipamento_id, 'Equipamento')
|
||||||
|
: null,
|
||||||
|
status: query.status || null,
|
||||||
|
statusPagamento: query.status_pagamento || null,
|
||||||
|
tipo: query.tipo || null,
|
||||||
|
busca: query.busca ? String(query.busca).trim() : null,
|
||||||
|
cobrancaAberta:
|
||||||
|
query.cobranca_aberta === '1' ||
|
||||||
|
query.cobranca_aberta === 'true'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadReportOrders(query) {
|
||||||
|
const filtros = buildReportFilters(query);
|
||||||
|
const where = [
|
||||||
|
'os.excluido_em IS NULL',
|
||||||
|
"os.status NOT IN ('RASCUNHO', 'CANCELADA')",
|
||||||
|
`${filtros.criterio.coluna} >= ?`,
|
||||||
|
`${filtros.criterio.coluna} < DATE_ADD(?, INTERVAL 1 DAY)`
|
||||||
|
];
|
||||||
|
|
||||||
|
const params = [
|
||||||
|
filtros.dataInicioSql,
|
||||||
|
filtros.dataFimSql
|
||||||
|
];
|
||||||
|
|
||||||
|
if (filtros.clienteId) {
|
||||||
|
where.push('os.cliente_id = ?');
|
||||||
|
params.push(filtros.clienteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filtros.equipamentoId) {
|
||||||
|
where.push('os.equipamento_id = ?');
|
||||||
|
params.push(filtros.equipamentoId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filtros.status) {
|
||||||
|
where.push('os.status = ?');
|
||||||
|
params.push(filtros.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filtros.statusPagamento) {
|
||||||
|
where.push('os.status_pagamento = ?');
|
||||||
|
params.push(filtros.statusPagamento);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filtros.tipo) {
|
||||||
|
where.push('os.tipo = ?');
|
||||||
|
params.push(filtros.tipo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filtros.cobrancaAberta) {
|
||||||
|
where.push("os.status_pagamento IN ('PENDENTE', 'PARCIAL')");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filtros.busca) {
|
||||||
|
const search = `%${filtros.busca}%`;
|
||||||
|
where.push(`(
|
||||||
|
CAST(os.id AS CHAR) LIKE ? OR
|
||||||
|
os.equipamento_nome_snapshot LIKE ? OR
|
||||||
|
os.numero_serie_snapshot LIKE ? OR
|
||||||
|
os.problema_relatado LIKE ? OR
|
||||||
|
os.cliente_nome_snapshot LIKE ?
|
||||||
|
)`);
|
||||||
|
|
||||||
|
params.push(search, search, search, search, search);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { results } = await dbQuery(
|
||||||
|
`
|
||||||
|
SELECT
|
||||||
|
os.id,
|
||||||
|
os.cliente_id,
|
||||||
|
os.cliente_nome_snapshot,
|
||||||
|
os.equipamento_nome_snapshot,
|
||||||
|
os.numero_serie_snapshot,
|
||||||
|
os.tipo,
|
||||||
|
os.status,
|
||||||
|
os.status_pagamento,
|
||||||
|
os.data_entrada,
|
||||||
|
os.data_finalizacao,
|
||||||
|
os.data_retirada,
|
||||||
|
os.total_horas_expediente,
|
||||||
|
os.total_horas_extras,
|
||||||
|
os.valor_mao_obra,
|
||||||
|
os.valor_insumos,
|
||||||
|
os.valor_desconto,
|
||||||
|
os.valor_acrescimo,
|
||||||
|
os.valor_total,
|
||||||
|
os.valor_pago,
|
||||||
|
GREATEST(os.valor_total - os.valor_pago, 0) AS valor_pendente
|
||||||
|
FROM manut_ordens_servico os
|
||||||
|
WHERE ${where.join(' AND ')}
|
||||||
|
ORDER BY
|
||||||
|
os.cliente_nome_snapshot,
|
||||||
|
${filtros.criterio.coluna},
|
||||||
|
os.id;
|
||||||
|
`,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
|
||||||
|
let clienteNome = null;
|
||||||
|
|
||||||
|
if (filtros.clienteId) {
|
||||||
|
const { results: clientes } = await dbQuery(
|
||||||
|
`
|
||||||
|
SELECT nome_fantasia
|
||||||
|
FROM manut_clientes
|
||||||
|
WHERE id = ?
|
||||||
|
LIMIT 1;
|
||||||
|
`,
|
||||||
|
[filtros.clienteId]
|
||||||
|
);
|
||||||
|
|
||||||
|
clienteNome = clientes.length
|
||||||
|
? clientes[0].nome_fantasia
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
ordens: results,
|
||||||
|
filtros: {
|
||||||
|
dataInicio: filtros.dataInicio,
|
||||||
|
dataFim: filtros.dataFim,
|
||||||
|
criterioLabel: filtros.criterio.label,
|
||||||
|
clienteNome
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var routes = function () {
|
||||||
|
router.get('/downloadOrdemServicoPdf/:id', async function (req, res) {
|
||||||
|
try {
|
||||||
|
const id = positiveInteger(req.params.id, 'Ordem de serviço');
|
||||||
|
const interno =
|
||||||
|
req.query.interno === '1' ||
|
||||||
|
req.query.interno === 'true';
|
||||||
|
|
||||||
|
const ordem = await loadFullOrder(id);
|
||||||
|
|
||||||
|
gerarOrdemServicoPdf(res, ordem, {
|
||||||
|
interno
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
sendError(req, res, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/downloadRelatorioOrdensServicoPdf', async function (req, res) {
|
||||||
|
try {
|
||||||
|
const resultado = await loadReportOrders(req.query || {});
|
||||||
|
|
||||||
|
gerarRelatorioOrdensServicoPdf(
|
||||||
|
res,
|
||||||
|
resultado.ordens,
|
||||||
|
resultado.filtros
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
sendError(req, res, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return router;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = routes;
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -203,6 +203,16 @@
|
||||||
"yargs": "^16.2.0"
|
"yargs": "^16.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@noble/ciphers": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw=="
|
||||||
|
},
|
||||||
|
"@noble/hashes": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
|
||||||
|
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="
|
||||||
|
},
|
||||||
"@panva/asn1.js": {
|
"@panva/asn1.js": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz",
|
||||||
|
|
@ -298,6 +308,21 @@
|
||||||
"@sendgrid/helpers": "^8.0.0"
|
"@sendgrid/helpers": "^8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@swc/helpers": {
|
||||||
|
"version": "0.5.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz",
|
||||||
|
"integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.8.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@tootallnate/once": {
|
"@tootallnate/once": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
||||||
|
|
@ -510,8 +535,7 @@
|
||||||
"base64-js": {
|
"base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"bignumber.js": {
|
"bignumber.js": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.4",
|
||||||
|
|
@ -569,6 +593,29 @@
|
||||||
"fill-range": "^7.1.1"
|
"fill-range": "^7.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"brotli": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==",
|
||||||
|
"requires": {
|
||||||
|
"base64-js": "^1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"browserify-zlib": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
|
||||||
|
"requires": {
|
||||||
|
"pako": "~1.0.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"pako": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"buffer-equal-constant-time": {
|
"buffer-equal-constant-time": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
||||||
|
|
@ -605,6 +652,11 @@
|
||||||
"wrap-ansi": "^7.0.0"
|
"wrap-ansi": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"clone": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="
|
||||||
|
},
|
||||||
"color-convert": {
|
"color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
|
@ -738,6 +790,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
|
||||||
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
|
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
|
||||||
},
|
},
|
||||||
|
"dfa": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q=="
|
||||||
|
},
|
||||||
"dicer": {
|
"dicer": {
|
||||||
"version": "0.3.1",
|
"version": "0.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.1.tgz",
|
||||||
|
|
@ -997,8 +1054,7 @@
|
||||||
"fast-deep-equal": {
|
"fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"fast-text-encoding": {
|
"fast-text-encoding": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
|
|
@ -1113,6 +1169,22 @@
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA=="
|
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA=="
|
||||||
},
|
},
|
||||||
|
"fontkit": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==",
|
||||||
|
"requires": {
|
||||||
|
"@swc/helpers": "^0.5.12",
|
||||||
|
"brotli": "^1.3.2",
|
||||||
|
"clone": "^2.1.2",
|
||||||
|
"dfa": "^1.2.0",
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"restructure": "^3.0.0",
|
||||||
|
"tiny-inflate": "^1.0.3",
|
||||||
|
"unicode-properties": "^1.4.0",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"form-data": {
|
"form-data": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||||
|
|
@ -1571,6 +1643,11 @@
|
||||||
"@panva/asn1.js": "^1.0.0"
|
"@panva/asn1.js": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"js-md5": {
|
||||||
|
"version": "0.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.8.3.tgz",
|
||||||
|
"integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ=="
|
||||||
|
},
|
||||||
"json-bigint": {
|
"json-bigint": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
|
||||||
|
|
@ -1671,6 +1748,22 @@
|
||||||
"resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
|
||||||
"integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA=="
|
"integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA=="
|
||||||
},
|
},
|
||||||
|
"linebreak": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==",
|
||||||
|
"requires": {
|
||||||
|
"base64-js": "0.0.8",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
|
||||||
|
"integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.10",
|
"version": "4.17.10",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
|
||||||
|
|
@ -1976,6 +2069,11 @@
|
||||||
"yocto-queue": "^0.1.0"
|
"yocto-queue": "^0.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"pako": {
|
||||||
|
"version": "0.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
|
||||||
|
"integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="
|
||||||
|
},
|
||||||
"parseurl": {
|
"parseurl": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
|
||||||
|
|
@ -1996,11 +2094,32 @@
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||||
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||||
},
|
},
|
||||||
|
"pdfkit": {
|
||||||
|
"version": "0.19.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.19.1.tgz",
|
||||||
|
"integrity": "sha512-6Gzk+wDwTs4VSxsR5rCMTnIl5nlmkye1oWB0l2hDB1EX6ZNSIBroKQEv+2+fPPn+stVjyqzmsqRJVDfB9fo5DA==",
|
||||||
|
"requires": {
|
||||||
|
"@noble/ciphers": "^1.0.0",
|
||||||
|
"@noble/hashes": "^1.6.0",
|
||||||
|
"fontkit": "^2.0.4",
|
||||||
|
"js-md5": "^0.8.3",
|
||||||
|
"linebreak": "^1.1.0",
|
||||||
|
"png-js": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"picomatch": {
|
"picomatch": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||||
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="
|
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="
|
||||||
},
|
},
|
||||||
|
"png-js": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/png-js/-/png-js-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-PM/uYGzGdNSzqeOgly68+6wKQDL1SY0a/N+OEa/+br6LnHWOAJB0Npiamnodfq3jd2LS/i2fMeOKSAILjA+m5Q==",
|
||||||
|
"requires": {
|
||||||
|
"browserify-zlib": "^0.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"process-nextick-args": {
|
"process-nextick-args": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
|
||||||
|
|
@ -2147,6 +2266,11 @@
|
||||||
"path-parse": "^1.0.6"
|
"path-parse": "^1.0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"restructure": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw=="
|
||||||
|
},
|
||||||
"retry": {
|
"retry": {
|
||||||
"version": "0.13.1",
|
"version": "0.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
|
||||||
|
|
@ -2354,6 +2478,11 @@
|
||||||
"uuid": "^8.0.0"
|
"uuid": "^8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tiny-inflate": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="
|
||||||
|
},
|
||||||
"to-regex-range": {
|
"to-regex-range": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
|
|
@ -2400,6 +2529,24 @@
|
||||||
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
||||||
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
|
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
|
||||||
},
|
},
|
||||||
|
"unicode-properties": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==",
|
||||||
|
"requires": {
|
||||||
|
"base64-js": "^1.3.0",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unicode-trie": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==",
|
||||||
|
"requires": {
|
||||||
|
"pako": "^0.2.5",
|
||||||
|
"tiny-inflate": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"unique-string": {
|
"unique-string": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
"mysql": "^2.15.0",
|
"mysql": "^2.15.0",
|
||||||
"nodemailer": "^6.10.1",
|
"nodemailer": "^6.10.1",
|
||||||
"nodemon": "^3.1.14",
|
"nodemon": "^3.1.14",
|
||||||
|
"pdfkit": "^0.19.1",
|
||||||
"shelljs": "^0.8.4",
|
"shelljs": "^0.8.4",
|
||||||
"unzipper": "^0.12.3"
|
"unzipper": "^0.12.3"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ app.use('/email', require('./controllers/emailController').router);
|
||||||
app.use('/checkout', require('./controllers/checkoutController')());
|
app.use('/checkout', require('./controllers/checkoutController')());
|
||||||
|
|
||||||
app.use('/otp', require('./controllers/oriontardproController')());
|
app.use('/otp', require('./controllers/oriontardproController')());
|
||||||
|
app.use('/otp', require('./controllers/oriontard.pdf.controller')());
|
||||||
|
|
||||||
|
|
||||||
// Allteeth
|
// Allteeth
|
||||||
app.use('/api_5/sincronia', require('./controllers/allteeth/sincroniaController')());
|
app.use('/api_5/sincronia', require('./controllers/allteeth/sincroniaController')());
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -26,7 +26,7 @@ import { BingoSortComponent } from './bingo-sort/bingo-sort.component';
|
||||||
import { BingoComponent } from './bingo/bingo.component';
|
import { BingoComponent } from './bingo/bingo.component';
|
||||||
import { BingoDashboardComponent } from './bingo-dashboard/bingo-dashboard.component';
|
import { BingoDashboardComponent } from './bingo-dashboard/bingo-dashboard.component';
|
||||||
import { BingoService } from './services/bingoService';
|
import { BingoService } from './services/bingoService';
|
||||||
import { OrionDashboardComponent } from './orion-dashboard/orion-dashboard.component';
|
import { OrionDashboardComponent } from './oriontard/orion-dashboard/orion-dashboard.component';
|
||||||
import { OrionService } from './services/orionService';
|
import { OrionService } from './services/orionService';
|
||||||
import { ProdutosComponent } from './produtos/produtos.component';
|
import { ProdutosComponent } from './produtos/produtos.component';
|
||||||
import { ProdutosService } from './services/produtosService';
|
import { ProdutosService } from './services/produtosService';
|
||||||
|
|
@ -36,6 +36,8 @@ import { AgrobaseService } from './services/agrobase.service';
|
||||||
import { TermosUsoComponent } from './termos-uso/termos-uso.component';
|
import { TermosUsoComponent } from './termos-uso/termos-uso.component';
|
||||||
import { PoliticaPrivacidadeComponent } from './politica-privacidade/politica-privacidade.component';
|
import { PoliticaPrivacidadeComponent } from './politica-privacidade/politica-privacidade.component';
|
||||||
import { SobreComponent } from './sobre/sobre.component';
|
import { SobreComponent } from './sobre/sobre.component';
|
||||||
|
import { OrionOrdensServicoComponent } from './oriontard/orion-ordens-servico/orion-ordens-servico.component';
|
||||||
|
import { OrionOrdemServicoFormComponent } from './oriontard/orion-ordem-servico-form/orion-ordem-servico-form.component';
|
||||||
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
|
|
@ -149,10 +151,24 @@ const routes: Routes = [
|
||||||
component: BingoSortComponent,
|
component: BingoSortComponent,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}, {
|
},
|
||||||
path: 'orion-dashboard',
|
{
|
||||||
|
path: 'orion-dashboard',
|
||||||
component: OrionDashboardComponent
|
component: OrionDashboardComponent
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
|
path: 'orion-ordens-servico/nova',
|
||||||
|
component: OrionOrdemServicoFormComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'orion-ordens-servico/:id/editar',
|
||||||
|
component: OrionOrdemServicoFormComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'orion-ordens-servico',
|
||||||
|
component: OrionOrdensServicoComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
path: 'agrobase',
|
path: 'agrobase',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import { EasyAVAComponent } from './easy-ava/easy-ava.component';
|
||||||
import { BingoSortComponent } from './bingo-sort/bingo-sort.component';
|
import { BingoSortComponent } from './bingo-sort/bingo-sort.component';
|
||||||
import { BingoComponent } from './bingo/bingo.component';
|
import { BingoComponent } from './bingo/bingo.component';
|
||||||
import { BingoDashboardComponent } from './bingo-dashboard/bingo-dashboard.component';
|
import { BingoDashboardComponent } from './bingo-dashboard/bingo-dashboard.component';
|
||||||
import { OrionDashboardComponent } from './orion-dashboard/orion-dashboard.component';
|
import { OrionDashboardComponent } from './oriontard/orion-dashboard/orion-dashboard.component';
|
||||||
|
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||||
|
|
@ -49,6 +49,8 @@ import { TipoArquivoNomePipe } from './pipes/tipo-arquivo-nome.pipe';
|
||||||
import { SobreComponent } from './sobre/sobre.component';
|
import { SobreComponent } from './sobre/sobre.component';
|
||||||
import { PoliticaPrivacidadeComponent } from './politica-privacidade/politica-privacidade.component';
|
import { PoliticaPrivacidadeComponent } from './politica-privacidade/politica-privacidade.component';
|
||||||
import { TermosUsoComponent } from './termos-uso/termos-uso.component';
|
import { TermosUsoComponent } from './termos-uso/termos-uso.component';
|
||||||
|
import { OrionOrdensServicoComponent } from './oriontard/orion-ordens-servico/orion-ordens-servico.component';
|
||||||
|
import { OrionOrdemServicoFormComponent } from './oriontard/orion-ordem-servico-form/orion-ordem-servico-form.component';
|
||||||
|
|
||||||
|
|
||||||
// Registrar a localidade pt-BR
|
// Registrar a localidade pt-BR
|
||||||
|
|
@ -79,7 +81,7 @@ registerLocaleData(localePt);
|
||||||
ProdutosComponent,
|
ProdutosComponent,
|
||||||
AgrobaseLoginComponent,
|
AgrobaseLoginComponent,
|
||||||
AgrobaseVersionamentoComponent,
|
AgrobaseVersionamentoComponent,
|
||||||
TipoArquivoNomePipe, SobreComponent, PoliticaPrivacidadeComponent, TermosUsoComponent
|
TipoArquivoNomePipe, SobreComponent, PoliticaPrivacidadeComponent, TermosUsoComponent, OrionOrdensServicoComponent, OrionOrdemServicoFormComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ export class ChavesComponent implements OnInit {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (confirm(Mensagem)) {
|
if (confirm(Mensagem)) {
|
||||||
this.GerarChave(90);
|
this.GerarChave(30);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -149,12 +149,33 @@ export class DashboardComponent implements OnInit {
|
||||||
{
|
{
|
||||||
index: 5,
|
index: 5,
|
||||||
texto: "OrionTard Pro",
|
texto: "OrionTard Pro",
|
||||||
link: "/orion-dashboard",
|
link: "",
|
||||||
havesub: false,
|
havesub: true,
|
||||||
dropicon: "arrow_drop_down",
|
dropicon: "arrow_drop_down",
|
||||||
icon: "home",
|
icon: "home",
|
||||||
issub: false,
|
issub: false,
|
||||||
sub: null,
|
sub: [
|
||||||
|
{
|
||||||
|
index: 0,
|
||||||
|
texto: "Logs",
|
||||||
|
link: "/orion-dashboard",
|
||||||
|
icon: "update",
|
||||||
|
havesub: false,
|
||||||
|
dropicon: "",
|
||||||
|
issub: true,
|
||||||
|
sub: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 1,
|
||||||
|
texto: "Ordens de Serviço",
|
||||||
|
link: "/orion-ordens-servico",
|
||||||
|
icon: "update",
|
||||||
|
havesub: false,
|
||||||
|
dropicon: "",
|
||||||
|
issub: true,
|
||||||
|
sub: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
index: 6,
|
index: 6,
|
||||||
|
|
|
||||||
|
|
@ -1,92 +1,513 @@
|
||||||
export class OrionLogsModel {
|
export class OrionLogsModel {
|
||||||
dataLog: Date;
|
dataLog: Date;
|
||||||
nome: string;
|
nome: string;
|
||||||
coordenadas: string;
|
coordenadas: string;
|
||||||
perfil: string;
|
perfil: string;
|
||||||
log: string;
|
log: string;
|
||||||
logObj: OrionLogsDetalhesModel;
|
logObj: OrionLogsDetalhesModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OrionLogsDetalhesModel {
|
export class OrionLogsDetalhesModel {
|
||||||
data: Date;
|
data: Date;
|
||||||
firmwareVersao: number;
|
firmwareVersao: number;
|
||||||
robotNs: number;
|
robotNs: number;
|
||||||
modelo: string;
|
modelo: string;
|
||||||
versao: string;
|
versao: string;
|
||||||
cpf: string;
|
cpf: string;
|
||||||
usuario: string;
|
usuario: string;
|
||||||
perfil: string;
|
perfil: string;
|
||||||
camera: string;
|
camera: string;
|
||||||
servico: OrionLogServicoModel;
|
servico: OrionLogServicoModel;
|
||||||
odometroTotal: number;
|
odometroTotal: number;
|
||||||
odometroConectado: number;
|
odometroConectado: number;
|
||||||
odometroMotores: number;
|
odometroMotores: number;
|
||||||
odometroMotoresParcial: number;
|
odometroMotoresParcial: number;
|
||||||
latitude: number;
|
latitude: number;
|
||||||
longitude: number;
|
longitude: number;
|
||||||
altitude: number;
|
altitude: number;
|
||||||
tempoTotal: number;
|
tempoTotal: number;
|
||||||
tempoConectado: number;
|
tempoConectado: number;
|
||||||
sensores: OrionLogSensorModel[];
|
sensores: OrionLogSensorModel[];
|
||||||
portaCOM: string;
|
portaCOM: string;
|
||||||
baudRate: number;
|
baudRate: number;
|
||||||
conectado: boolean;
|
conectado: boolean;
|
||||||
distancia: number;
|
distancia: number;
|
||||||
|
|
||||||
HoraInicio: string;
|
HoraInicio: string;
|
||||||
HoraFim: string;
|
HoraFim: string;
|
||||||
Duracao: string;
|
Duracao: string;
|
||||||
Coordenadas: string;
|
Coordenadas: string;
|
||||||
Localizacao: string;
|
Localizacao: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OrionLogServicoModel {
|
export class OrionLogServicoModel {
|
||||||
Nome: string;
|
Nome: string;
|
||||||
Cidade: string;
|
Cidade: string;
|
||||||
Responsavel: string;
|
Responsavel: string;
|
||||||
Servico: string;
|
Servico: string;
|
||||||
Path: string;
|
Path: string;
|
||||||
DataCadastro: Date;
|
DataCadastro: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OrionLogSensorModel {
|
export class OrionLogSensorModel {
|
||||||
sensor: number;
|
sensor: number;
|
||||||
descricao: string;
|
descricao: string;
|
||||||
minimo: number;
|
minimo: number;
|
||||||
maximo: number;
|
maximo: number;
|
||||||
media: number;
|
media: number;
|
||||||
moda: number;
|
moda: number;
|
||||||
mediana: number;
|
mediana: number;
|
||||||
lista: number[];
|
lista: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GeoCoordenadasResponseModel {
|
export class GeoCoordenadasResponseModel {
|
||||||
data: GeoCoordenadasModel[];
|
data: GeoCoordenadasModel[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GeoCoordenadasModel {
|
export class GeoCoordenadasModel {
|
||||||
latitude: number;
|
latitude: number;
|
||||||
longitude: number;
|
longitude: number;
|
||||||
type: string;
|
type: string;
|
||||||
distance: number;
|
distance: number;
|
||||||
name: string;
|
name: string;
|
||||||
number: string;
|
number: string;
|
||||||
postal_code: string;
|
postal_code: string;
|
||||||
street: string;
|
street: string;
|
||||||
confidence: number;
|
confidence: number;
|
||||||
region: string;
|
region: string;
|
||||||
region_code: string;
|
region_code: string;
|
||||||
county: string;
|
county: string;
|
||||||
lacality: string;
|
lacality: string;
|
||||||
administrative_area: string;
|
administrative_area: string;
|
||||||
neighborhood: string;
|
neighborhood: string;
|
||||||
country: string;
|
country: string;
|
||||||
country_code: string;
|
country_code: string;
|
||||||
continent: string;
|
continent: string;
|
||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LogAgrupado {
|
export class LogAgrupado {
|
||||||
key: string;
|
key: string;
|
||||||
value: OrionLogsDetalhesModel[];
|
value: OrionLogsDetalhesModel[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// RESPOSTA PADRÃO DA API
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
export interface OrionApiErrorModel {
|
||||||
|
code?: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrionApiResponseModel<T> {
|
||||||
|
status: number;
|
||||||
|
error: OrionApiErrorModel | null;
|
||||||
|
response: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// CADASTROS AUXILIARES
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
export interface ClienteManutencaoModel {
|
||||||
|
id: number;
|
||||||
|
nome_fantasia: string;
|
||||||
|
razao_social?: string;
|
||||||
|
documento?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EquipamentoManutencaoModel {
|
||||||
|
id: number;
|
||||||
|
cliente_id?: number;
|
||||||
|
modelo_id?: number;
|
||||||
|
codigo_interno?: string;
|
||||||
|
nome: string;
|
||||||
|
numero_serie: string;
|
||||||
|
versao?: string;
|
||||||
|
|
||||||
|
odometro_total_segundos: number;
|
||||||
|
odometro_conectado_segundos: number;
|
||||||
|
odometro_movimento_segundos: number;
|
||||||
|
odometro_parcial_segundos: number;
|
||||||
|
odometro_atualizado_em?: string;
|
||||||
|
|
||||||
|
modelo_nome?: string;
|
||||||
|
cliente_nome?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TecnicoManutencaoModel {
|
||||||
|
id: number;
|
||||||
|
usuario_sistema_id?: number;
|
||||||
|
nome: string;
|
||||||
|
especialidade?: string;
|
||||||
|
valor_hora_expediente_padrao: number;
|
||||||
|
valor_hora_extra_padrao: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MaterialManutencaoModel {
|
||||||
|
id: number;
|
||||||
|
codigo?: string;
|
||||||
|
descricao: string;
|
||||||
|
tipo: MaterialManutencaoTipo;
|
||||||
|
unidade: string;
|
||||||
|
custo_padrao: number;
|
||||||
|
preco_venda_padrao: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MaterialManutencaoTipo =
|
||||||
|
| 'PECA'
|
||||||
|
| 'INSUMO'
|
||||||
|
| 'SERVICO_TERCEIRO'
|
||||||
|
| 'FRETE'
|
||||||
|
| 'FERRAMENTA'
|
||||||
|
| 'OUTRO';
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// ORDENS DE SERVIÇO
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
export interface OrdemServicoListaModel {
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
cliente_id?: number;
|
||||||
|
equipamento_id?: number;
|
||||||
|
tecnico_responsavel_id?: number;
|
||||||
|
|
||||||
|
cliente_nome_snapshot?: string;
|
||||||
|
equipamento_nome_snapshot?: string;
|
||||||
|
numero_serie_snapshot?: string;
|
||||||
|
modelo_snapshot?: string;
|
||||||
|
versao_snapshot?: string;
|
||||||
|
|
||||||
|
tipo: OrdemServicoTipo;
|
||||||
|
status: OrdemServicoStatus;
|
||||||
|
status_pagamento: OrdemServicoStatusPagamento;
|
||||||
|
prioridade: OrdemServicoPrioridade;
|
||||||
|
|
||||||
|
data_entrada: string;
|
||||||
|
data_previsao?: string;
|
||||||
|
data_finalizacao?: string;
|
||||||
|
data_retirada?: string;
|
||||||
|
|
||||||
|
problema_relatado?: string;
|
||||||
|
|
||||||
|
total_horas_expediente: number;
|
||||||
|
total_horas_extras: number;
|
||||||
|
|
||||||
|
valor_mao_obra: number;
|
||||||
|
valor_insumos: number;
|
||||||
|
valor_desconto: number;
|
||||||
|
valor_acrescimo: number;
|
||||||
|
valor_total: number;
|
||||||
|
valor_pago: number;
|
||||||
|
valor_pendente: number;
|
||||||
|
|
||||||
|
tecnico_responsavel_nome?: string;
|
||||||
|
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrdemServicoPaginacaoModel {
|
||||||
|
page: number;
|
||||||
|
pageSize: number;
|
||||||
|
total: number;
|
||||||
|
totalPages: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrdemServicoListaResponseModel {
|
||||||
|
items: OrdemServicoListaModel[];
|
||||||
|
pagination: OrdemServicoPaginacaoModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrdemServicoFiltrosModel {
|
||||||
|
page?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
busca?: string;
|
||||||
|
status?: OrdemServicoStatus | '';
|
||||||
|
status_pagamento?: OrdemServicoStatusPagamento | '';
|
||||||
|
tipo?: OrdemServicoTipo | '';
|
||||||
|
equipamento_id?: number;
|
||||||
|
cliente_id?: number;
|
||||||
|
data_inicio?: string;
|
||||||
|
data_fim?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrdemServicoDetalhesModel extends OrdemServicoListaModel {
|
||||||
|
diagnostico?: string;
|
||||||
|
solucao_resumo?: string;
|
||||||
|
|
||||||
|
responsavel_entrega?: string;
|
||||||
|
responsavel_retirada?: string;
|
||||||
|
|
||||||
|
observacoes_internas?: string;
|
||||||
|
observacoes_cliente?: string;
|
||||||
|
|
||||||
|
garantia_dias?: number;
|
||||||
|
garantia_ate?: string;
|
||||||
|
|
||||||
|
custo_insumos: number;
|
||||||
|
justificativa_ajuste_valor?: string;
|
||||||
|
origem?: string;
|
||||||
|
|
||||||
|
atividades: OrdemServicoAtividadeModel[];
|
||||||
|
odometros_da_os: OrdemServicoOdometroModel[];
|
||||||
|
intervalo_desde_leitura_anterior?: OrdemServicoIntervaloOdometroModel;
|
||||||
|
pagamentos: OrdemServicoPagamentoModel[];
|
||||||
|
historico_status: OrdemServicoHistoricoStatusModel[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrdemServicoAtividadeModel {
|
||||||
|
id: number;
|
||||||
|
ordem_servico_id: number;
|
||||||
|
|
||||||
|
data_atividade: string;
|
||||||
|
descricao: string;
|
||||||
|
ordem_exibicao: number;
|
||||||
|
|
||||||
|
horas_expediente: number;
|
||||||
|
horas_extras: number;
|
||||||
|
valor_hora_expediente: number;
|
||||||
|
valor_hora_extra: number;
|
||||||
|
valor_mao_obra: number;
|
||||||
|
|
||||||
|
cobravel: boolean | number;
|
||||||
|
|
||||||
|
observacoes_internas?: string;
|
||||||
|
observacoes_cliente?: string;
|
||||||
|
|
||||||
|
tecnicos: OrdemServicoAtividadeTecnicoModel[];
|
||||||
|
materiais: OrdemServicoAtividadeMaterialModel[];
|
||||||
|
|
||||||
|
created_at?: string;
|
||||||
|
updated_at?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrdemServicoAtividadeTecnicoModel {
|
||||||
|
atividade_id?: number;
|
||||||
|
tecnico_id: number;
|
||||||
|
tecnico_nome?: string;
|
||||||
|
papel: OrdemServicoTecnicoPapel;
|
||||||
|
horas_trabalhadas_expediente?: number;
|
||||||
|
horas_trabalhadas_extras?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrdemServicoAtividadeMaterialModel {
|
||||||
|
id?: number;
|
||||||
|
atividade_id?: number;
|
||||||
|
material_id?: number;
|
||||||
|
|
||||||
|
descricao_snapshot: string;
|
||||||
|
unidade_snapshot: string;
|
||||||
|
|
||||||
|
quantidade: number;
|
||||||
|
valor_custo_unitario: number;
|
||||||
|
valor_cobrado_unitario: number;
|
||||||
|
valor_custo_total?: number;
|
||||||
|
valor_cobrado_total?: number;
|
||||||
|
|
||||||
|
cobravel: boolean | number;
|
||||||
|
observacoes?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrdemServicoOdometroModel {
|
||||||
|
id?: number;
|
||||||
|
equipamento_id?: number;
|
||||||
|
ordem_servico_id?: number;
|
||||||
|
|
||||||
|
tipo_leitura: OrdemServicoTipoLeituraOdometro;
|
||||||
|
origem?: string;
|
||||||
|
leitura_em?: string;
|
||||||
|
|
||||||
|
odometro_total_segundos: number;
|
||||||
|
odometro_conectado_segundos: number;
|
||||||
|
odometro_movimento_segundos: number;
|
||||||
|
odometro_parcial_segundos: number;
|
||||||
|
|
||||||
|
odometro_total_anterior?: number;
|
||||||
|
odometro_conectado_anterior?: number;
|
||||||
|
odometro_movimento_anterior?: number;
|
||||||
|
|
||||||
|
observacoes?: string;
|
||||||
|
usuario_id?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrdemServicoIntervaloOdometroModel {
|
||||||
|
total_segundos?: number;
|
||||||
|
conectado_segundos?: number;
|
||||||
|
movimento_segundos?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrdemServicoPagamentoModel {
|
||||||
|
id?: number;
|
||||||
|
ordem_servico_id?: number;
|
||||||
|
data_pagamento?: string;
|
||||||
|
valor: number;
|
||||||
|
forma_pagamento: OrdemServicoFormaPagamento;
|
||||||
|
referencia?: string;
|
||||||
|
observacoes?: string;
|
||||||
|
usuario_id?: number;
|
||||||
|
cancelado_em?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrdemServicoHistoricoStatusModel {
|
||||||
|
id: number;
|
||||||
|
ordem_servico_id: number;
|
||||||
|
status_anterior?: OrdemServicoStatus;
|
||||||
|
status_novo: OrdemServicoStatus;
|
||||||
|
observacoes?: string;
|
||||||
|
alterado_por_usuario_id?: number;
|
||||||
|
created_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrdemServicoSalvarModel {
|
||||||
|
cliente_id?: number;
|
||||||
|
equipamento_id?: number;
|
||||||
|
tecnico_responsavel_id?: number;
|
||||||
|
|
||||||
|
equipamento_nome?: string;
|
||||||
|
numero_serie?: string;
|
||||||
|
modelo?: string;
|
||||||
|
versao?: string;
|
||||||
|
|
||||||
|
tipo?: OrdemServicoTipo;
|
||||||
|
status?: OrdemServicoStatus;
|
||||||
|
status_pagamento?: OrdemServicoStatusPagamento;
|
||||||
|
prioridade?: OrdemServicoPrioridade;
|
||||||
|
|
||||||
|
data_entrada?: string;
|
||||||
|
data_previsao?: string;
|
||||||
|
data_finalizacao?: string;
|
||||||
|
data_retirada?: string;
|
||||||
|
|
||||||
|
problema_relatado?: string;
|
||||||
|
diagnostico?: string;
|
||||||
|
solucao_resumo?: string;
|
||||||
|
|
||||||
|
responsavel_entrega?: string;
|
||||||
|
responsavel_retirada?: string;
|
||||||
|
|
||||||
|
observacoes_internas?: string;
|
||||||
|
observacoes_cliente?: string;
|
||||||
|
|
||||||
|
garantia_dias?: number;
|
||||||
|
garantia_ate?: string;
|
||||||
|
|
||||||
|
valor_desconto?: number;
|
||||||
|
valor_acrescimo?: number;
|
||||||
|
justificativa_ajuste_valor?: string;
|
||||||
|
|
||||||
|
observacao_status?: string;
|
||||||
|
usuario_id?: number;
|
||||||
|
|
||||||
|
odometro_entrada?: OrdemServicoOdometroModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrdemServicoAtividadeSalvarModel {
|
||||||
|
data_atividade?: string;
|
||||||
|
descricao: string;
|
||||||
|
ordem_exibicao?: number;
|
||||||
|
|
||||||
|
horas_expediente?: number;
|
||||||
|
horas_extras?: number;
|
||||||
|
valor_hora_expediente?: number;
|
||||||
|
valor_hora_extra?: number;
|
||||||
|
cobravel?: boolean;
|
||||||
|
|
||||||
|
observacoes_internas?: string;
|
||||||
|
observacoes_cliente?: string;
|
||||||
|
usuario_id?: number;
|
||||||
|
|
||||||
|
tecnicos?: OrdemServicoAtividadeTecnicoModel[];
|
||||||
|
materiais?: OrdemServicoAtividadeMaterialModel[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrdemServicoTotaisModel {
|
||||||
|
total_horas_expediente: number;
|
||||||
|
total_horas_extras: number;
|
||||||
|
valor_mao_obra: number;
|
||||||
|
custo_insumos: number;
|
||||||
|
valor_insumos: number;
|
||||||
|
valor_desconto: number;
|
||||||
|
valor_acrescimo: number;
|
||||||
|
valor_total: number;
|
||||||
|
valor_pago: number;
|
||||||
|
valor_pendente: number;
|
||||||
|
status_pagamento: OrdemServicoStatusPagamento;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrdemServicoOperacaoResultModel {
|
||||||
|
id: number;
|
||||||
|
ordem_servico_id?: number;
|
||||||
|
equipamento_id?: number;
|
||||||
|
excluida?: boolean;
|
||||||
|
cancelado?: boolean;
|
||||||
|
tipo_leitura?: OrdemServicoTipoLeituraOdometro;
|
||||||
|
totais_os?: OrdemServicoTotaisModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrionLogInsertModel {
|
||||||
|
cpf?: string;
|
||||||
|
perfil?: string;
|
||||||
|
latitude?: number;
|
||||||
|
longitude?: number;
|
||||||
|
log: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OrdemServicoTipo =
|
||||||
|
| 'CORRETIVA'
|
||||||
|
| 'PREVENTIVA'
|
||||||
|
| 'REFORMA'
|
||||||
|
| 'DESENVOLVIMENTO'
|
||||||
|
| 'FABRICACAO'
|
||||||
|
| 'GARANTIA'
|
||||||
|
| 'CORTESIA'
|
||||||
|
| 'INTERNA'
|
||||||
|
| 'OUTRO';
|
||||||
|
|
||||||
|
export type OrdemServicoStatus =
|
||||||
|
| 'RASCUNHO'
|
||||||
|
| 'ABERTA'
|
||||||
|
| 'EM_DIAGNOSTICO'
|
||||||
|
| 'AGUARDANDO_APROVACAO'
|
||||||
|
| 'AGUARDANDO_PECA'
|
||||||
|
| 'EM_EXECUCAO'
|
||||||
|
| 'EM_TESTES'
|
||||||
|
| 'FINALIZADA'
|
||||||
|
| 'AGUARDANDO_RETIRADA'
|
||||||
|
| 'RETIRADA'
|
||||||
|
| 'CANCELADA';
|
||||||
|
|
||||||
|
export type OrdemServicoStatusPagamento =
|
||||||
|
| 'NAO_APLICAVEL'
|
||||||
|
| 'PENDENTE'
|
||||||
|
| 'PARCIAL'
|
||||||
|
| 'RECEBIDO'
|
||||||
|
| 'CORTESIA';
|
||||||
|
|
||||||
|
export type OrdemServicoPrioridade =
|
||||||
|
| 'BAIXA'
|
||||||
|
| 'NORMAL'
|
||||||
|
| 'ALTA'
|
||||||
|
| 'URGENTE';
|
||||||
|
|
||||||
|
export type OrdemServicoTecnicoPapel =
|
||||||
|
| 'RESPONSAVEL'
|
||||||
|
| 'EXECUTOR'
|
||||||
|
| 'APOIO';
|
||||||
|
|
||||||
|
export type OrdemServicoFormaPagamento =
|
||||||
|
| 'DINHEIRO'
|
||||||
|
| 'PIX'
|
||||||
|
| 'TRANSFERENCIA'
|
||||||
|
| 'BOLETO'
|
||||||
|
| 'CARTAO'
|
||||||
|
| 'OUTRO';
|
||||||
|
|
||||||
|
export type OrdemServicoTipoLeituraOdometro =
|
||||||
|
| 'CADASTRO_INICIAL'
|
||||||
|
| 'ENTRADA_MANUTENCAO'
|
||||||
|
| 'SAIDA_MANUTENCAO'
|
||||||
|
| 'ATUALIZACAO_MANUAL'
|
||||||
|
| 'TELEMETRIA';
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
import { MatPaginator } from '@angular/material/paginator';
|
import { MatPaginator } from '@angular/material/paginator';
|
||||||
import { Chart } from 'chart.js';
|
import { Chart } from 'chart.js';
|
||||||
import { LogAgrupado, OrionLogsDetalhesModel, OrionLogSensorModel, OrionLogsModel } from '../models/orionModel';
|
import { LogAgrupado, OrionLogsDetalhesModel, OrionLogSensorModel, OrionLogsModel } from '../../models/orionModel';
|
||||||
import { OrionService } from '../services/orionService';
|
import { OrionService } from '../../services/orionService';
|
||||||
import { MatTableDataSource } from '@angular/material/table';
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
import { MatTabChangeEvent } from '@angular/material';
|
import { MatTabChangeEvent } from '@angular/material';
|
||||||
|
|
||||||
|
|
@ -0,0 +1,720 @@
|
||||||
|
<div class="os-form-page">
|
||||||
|
|
||||||
|
<header class="page-header">
|
||||||
|
<div class="header-main">
|
||||||
|
<button mat-stroked-button type="button" (click)="voltar()">Voltar</button>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span class="eyebrow">ORIONTARD · MANUTENÇÃO</span>
|
||||||
|
<h1 *ngIf="!modoEdicao">Nova ordem de serviço</h1>
|
||||||
|
<h1 *ngIf="modoEdicao">OS #{{ ordemServicoId }}</h1>
|
||||||
|
<p *ngIf="!modoEdicao">Registre a entrada do equipamento e os odômetros recebidos.</p>
|
||||||
|
<p *ngIf="modoEdicao && ordem">
|
||||||
|
{{ ordem.equipamento_nome_snapshot || ordem.modelo_snapshot }}
|
||||||
|
<span *ngIf="ordem.numero_serie_snapshot">· NS {{ ordem.numero_serie_snapshot }}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="header-actions">
|
||||||
|
<ng-container *ngIf="modoEdicao && ordem">
|
||||||
|
<span class="status-badge" [ngClass]="classeStatus(ordem.status)">
|
||||||
|
{{ labelStatus(ordem.status) }}
|
||||||
|
</span>
|
||||||
|
<span class="status-badge" [ngClass]="classePagamento(ordem.status_pagamento)">
|
||||||
|
{{ labelPagamento(ordem.status_pagamento) }}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<button mat-raised-button color="primary" type="button"
|
||||||
|
(click)="salvarOrdem()" [disabled]="carregando || salvando">
|
||||||
|
{{ salvando ? 'Salvando...' : (modoEdicao ? 'Salvar alterações' : 'Criar OS') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
mat-stroked-button
|
||||||
|
type="button"
|
||||||
|
*ngIf="modoEdicao"
|
||||||
|
(click)="baixarPdfCliente()"
|
||||||
|
[disabled]="baixandoPdf || salvando">
|
||||||
|
{{ baixandoPdf ? 'Gerando PDF...' : 'Baixar OS' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="message message-error" *ngIf="erro">{{ erro }}</div>
|
||||||
|
<div class="message message-success" *ngIf="sucesso">{{ sucesso }}</div>
|
||||||
|
|
||||||
|
<div class="loading-card" *ngIf="carregando">
|
||||||
|
<div class="loading-bar"></div>
|
||||||
|
<span>Carregando dados da ordem de serviço...</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-container *ngIf="!carregando">
|
||||||
|
|
||||||
|
<form [formGroup]="formOrdem" class="section-card">
|
||||||
|
<div class="section-heading">
|
||||||
|
<div>
|
||||||
|
<span class="section-kicker">DADOS GERAIS</span>
|
||||||
|
<h2>Entrada e identificação</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-grid form-grid-4">
|
||||||
|
<mat-form-field appearance="outline" class="span-2">
|
||||||
|
<mat-label>Equipamento</mat-label>
|
||||||
|
<mat-select formControlName="equipamento_id"
|
||||||
|
(selectionChange)="equipamentoSelecionado($event.value)">
|
||||||
|
<mat-option *ngFor="let equipamento of equipamentos" [value]="equipamento.id">
|
||||||
|
{{ equipamento.nome }} · NS {{ equipamento.numero_serie }}
|
||||||
|
<span *ngIf="equipamento.modelo_nome">· {{ equipamento.modelo_nome }}</span>
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-error>Selecione o equipamento.</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Cliente</mat-label>
|
||||||
|
<mat-select formControlName="cliente_id">
|
||||||
|
<mat-option [value]="null">Não informado</mat-option>
|
||||||
|
<mat-option *ngFor="let cliente of clientes" [value]="cliente.id">
|
||||||
|
{{ cliente.nome_fantasia }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Técnico responsável</mat-label>
|
||||||
|
<mat-select formControlName="tecnico_responsavel_id">
|
||||||
|
<mat-option [value]="null">Não definido</mat-option>
|
||||||
|
<mat-option *ngFor="let tecnico of tecnicos" [value]="tecnico.id">
|
||||||
|
{{ tecnico.nome }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Tipo</mat-label>
|
||||||
|
|
||||||
|
<mat-select
|
||||||
|
formControlName="tipo"
|
||||||
|
(selectionChange)="tipoOrdemAlterado($event.value)">
|
||||||
|
|
||||||
|
<mat-option
|
||||||
|
*ngFor="let opcao of tipoOpcoes"
|
||||||
|
[value]="opcao.valor">
|
||||||
|
{{ opcao.texto }}
|
||||||
|
</mat-option>
|
||||||
|
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Situação financeira</mat-label>
|
||||||
|
|
||||||
|
<mat-select formControlName="status_pagamento" disabled>
|
||||||
|
<mat-option value="NAO_APLICAVEL">
|
||||||
|
Não aplicável
|
||||||
|
</mat-option>
|
||||||
|
|
||||||
|
<mat-option value="PENDENTE">
|
||||||
|
Pendente
|
||||||
|
</mat-option>
|
||||||
|
|
||||||
|
<mat-option value="PARCIAL">
|
||||||
|
Parcial
|
||||||
|
</mat-option>
|
||||||
|
|
||||||
|
<mat-option value="RECEBIDO">
|
||||||
|
Recebido
|
||||||
|
</mat-option>
|
||||||
|
|
||||||
|
<mat-option value="CORTESIA">
|
||||||
|
Cortesia
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Status</mat-label>
|
||||||
|
<mat-select formControlName="status">
|
||||||
|
<mat-option *ngFor="let opcao of statusOpcoes" [value]="opcao.valor">
|
||||||
|
{{ opcao.texto }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Prioridade</mat-label>
|
||||||
|
<mat-select formControlName="prioridade">
|
||||||
|
<mat-option *ngFor="let opcao of prioridadeOpcoes" [value]="opcao.valor">
|
||||||
|
{{ opcao.texto }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Data e hora de entrada</mat-label>
|
||||||
|
<input matInput type="datetime-local" formControlName="data_entrada">
|
||||||
|
<mat-error>Informe a data de entrada.</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Previsão</mat-label>
|
||||||
|
<input matInput type="datetime-local" formControlName="data_previsao">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Finalização</mat-label>
|
||||||
|
<input matInput type="datetime-local" formControlName="data_finalizacao">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Retirada</mat-label>
|
||||||
|
<input matInput type="datetime-local" formControlName="data_retirada">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Responsável pela entrega</mat-label>
|
||||||
|
<input matInput formControlName="responsavel_entrega">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Responsável pela retirada</mat-label>
|
||||||
|
<input matInput formControlName="responsavel_retirada">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="span-4">
|
||||||
|
<mat-label>Problema relatado pelo cliente</mat-label>
|
||||||
|
<textarea matInput rows="3" formControlName="problema_relatado"></textarea>
|
||||||
|
<mat-error>Descreva o problema relatado.</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="span-2">
|
||||||
|
<mat-label>Diagnóstico</mat-label>
|
||||||
|
<textarea matInput rows="4" formControlName="diagnostico"></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="span-2">
|
||||||
|
<mat-label>Solução resumida</mat-label>
|
||||||
|
<textarea matInput rows="4" formControlName="solucao_resumo"></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="span-2">
|
||||||
|
<mat-label>Observações internas</mat-label>
|
||||||
|
<textarea matInput rows="3" formControlName="observacoes_internas"></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="span-2">
|
||||||
|
<mat-label>Observações para o cliente</mat-label>
|
||||||
|
<textarea matInput rows="3" formControlName="observacoes_cliente"></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Garantia em dias</mat-label>
|
||||||
|
<input matInput type="number" min="0" formControlName="garantia_dias">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Garantia até</mat-label>
|
||||||
|
<input matInput type="date" formControlName="garantia_ate">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Desconto</mat-label>
|
||||||
|
<input matInput type="number" min="0" step="0.01" formControlName="valor_desconto">
|
||||||
|
<span matPrefix>R$ </span>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Acréscimo</mat-label>
|
||||||
|
<input matInput type="number" min="0" step="0.01" formControlName="valor_acrescimo">
|
||||||
|
<span matPrefix>R$ </span>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="span-2">
|
||||||
|
<mat-label>Justificativa do ajuste financeiro</mat-label>
|
||||||
|
<input matInput formControlName="justificativa_ajuste_valor">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="span-2">
|
||||||
|
<mat-label>Observação da alteração de status</mat-label>
|
||||||
|
<input matInput formControlName="observacao_status">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<section class="section-card" *ngIf="!modoEdicao" [formGroup]="formOdometroEntrada">
|
||||||
|
<div class="section-heading">
|
||||||
|
<div>
|
||||||
|
<span class="section-kicker">LEITURA DE ENTRADA</span>
|
||||||
|
<h2>Odômetros recebidos</h2>
|
||||||
|
<p>Confirme os valores exibidos pelo robô quando ele chegou ao laboratório.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-grid form-grid-4">
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Total em segundos</mat-label>
|
||||||
|
<input matInput type="number" min="0" formControlName="odometro_total_segundos">
|
||||||
|
<mat-hint>{{ segundosParaTempo(formOdometroEntrada.get('odometro_total_segundos').value) }}</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Conectado em segundos</mat-label>
|
||||||
|
<input matInput type="number" min="0" formControlName="odometro_conectado_segundos">
|
||||||
|
<mat-hint>{{ segundosParaTempo(formOdometroEntrada.get('odometro_conectado_segundos').value) }}</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Movimento em segundos</mat-label>
|
||||||
|
<input matInput type="number" min="0" formControlName="odometro_movimento_segundos">
|
||||||
|
<mat-hint>{{ segundosParaTempo(formOdometroEntrada.get('odometro_movimento_segundos').value) }}</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Parcial em segundos</mat-label>
|
||||||
|
<input matInput type="number" min="0" formControlName="odometro_parcial_segundos">
|
||||||
|
<mat-hint>{{ segundosParaTempo(formOdometroEntrada.get('odometro_parcial_segundos').value) }}</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Momento da leitura</mat-label>
|
||||||
|
<input matInput type="datetime-local" formControlName="leitura_em">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="span-3">
|
||||||
|
<mat-label>Observações da leitura</mat-label>
|
||||||
|
<input matInput formControlName="observacoes">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<mat-tab-group *ngIf="modoEdicao && ordem" class="os-tabs">
|
||||||
|
|
||||||
|
<mat-tab label="Atividades">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-toolbar">
|
||||||
|
<div>
|
||||||
|
<h2>Atividades realizadas</h2>
|
||||||
|
<p>Adicione um registro para cada dia ou etapa da manutenção.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button mat-raised-button color="primary" type="button"
|
||||||
|
(click)="abrirNovaAtividade()" *ngIf="!exibirFormAtividade">
|
||||||
|
Adicionar atividade
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="inline-form-card" [formGroup]="formAtividade" *ngIf="exibirFormAtividade">
|
||||||
|
<div class="inline-form-header">
|
||||||
|
<div>
|
||||||
|
<span class="section-kicker">ATIVIDADE</span>
|
||||||
|
<h3>{{ atividadeEditandoId ? 'Editar atividade' : 'Nova atividade' }}</h3>
|
||||||
|
</div>
|
||||||
|
<button mat-button type="button" (click)="cancelarEdicaoAtividade()">Cancelar</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-grid form-grid-4">
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Data</mat-label>
|
||||||
|
<input matInput type="date" formControlName="data_atividade">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="span-2">
|
||||||
|
<mat-label>Técnicos</mat-label>
|
||||||
|
<mat-select multiple formControlName="tecnico_ids">
|
||||||
|
<mat-option *ngFor="let tecnico of tecnicos" [value]="tecnico.id">
|
||||||
|
{{ tecnico.nome }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Ordem de exibição</mat-label>
|
||||||
|
<input matInput type="number" min="0" formControlName="ordem_exibicao">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="span-4">
|
||||||
|
<mat-label>Descrição do serviço realizado</mat-label>
|
||||||
|
<textarea matInput rows="4" formControlName="descricao"></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Horas de expediente</mat-label>
|
||||||
|
<input matInput type="number" min="0" step="0.25" formControlName="horas_expediente">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Horas extras</mat-label>
|
||||||
|
<input matInput type="number" min="0" step="0.25" formControlName="horas_extras">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Valor hora expediente</mat-label>
|
||||||
|
<input matInput type="number" min="0" step="0.01" formControlName="valor_hora_expediente">
|
||||||
|
<span matPrefix>R$ </span>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Valor hora extra</mat-label>
|
||||||
|
<input matInput type="number" min="0" step="0.01" formControlName="valor_hora_extra">
|
||||||
|
<span matPrefix>R$ </span>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<label class="checkbox-field span-4">
|
||||||
|
<input type="checkbox" formControlName="cobravel">
|
||||||
|
<span>Atividade cobrável</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="span-2">
|
||||||
|
<mat-label>Observações internas</mat-label>
|
||||||
|
<textarea matInput rows="2" formControlName="observacoes_internas"></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="span-2">
|
||||||
|
<mat-label>Observações para o cliente</mat-label>
|
||||||
|
<textarea matInput rows="2" formControlName="observacoes_cliente"></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="materials-section" formArrayName="materiais">
|
||||||
|
<div class="subsection-heading">
|
||||||
|
<div>
|
||||||
|
<h4>Materiais e insumos</h4>
|
||||||
|
<p>Use o catálogo ou registre um item avulso.</p>
|
||||||
|
</div>
|
||||||
|
<button mat-stroked-button type="button" (click)="adicionarMaterialAtividade()">
|
||||||
|
Adicionar material
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="material-row"
|
||||||
|
*ngFor="let materialControl of materiaisAtividade.controls; let i = index"
|
||||||
|
[formGroupName]="i">
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Catálogo</mat-label>
|
||||||
|
<mat-select formControlName="material_id"
|
||||||
|
(selectionChange)="materialCatalogoSelecionado(i, $event.value)">
|
||||||
|
<mat-option [value]="null">Item avulso</mat-option>
|
||||||
|
<mat-option *ngFor="let material of materiais" [value]="material.id">
|
||||||
|
{{ material.descricao }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="material-description">
|
||||||
|
<mat-label>Descrição</mat-label>
|
||||||
|
<input matInput formControlName="descricao_snapshot">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Unidade</mat-label>
|
||||||
|
<input matInput formControlName="unidade_snapshot">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Quantidade</mat-label>
|
||||||
|
<input matInput type="number" min="0.001" step="0.001" formControlName="quantidade">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Custo unitário</mat-label>
|
||||||
|
<input matInput type="number" min="0" step="0.01" formControlName="valor_custo_unitario">
|
||||||
|
<span matPrefix>R$ </span>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Valor cobrado</mat-label>
|
||||||
|
<input matInput type="number" min="0" step="0.01" formControlName="valor_cobrado_unitario">
|
||||||
|
<span matPrefix>R$ </span>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<label class="checkbox-field compact-checkbox">
|
||||||
|
<input type="checkbox" formControlName="cobravel">
|
||||||
|
<span>Cobrável</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<button mat-button type="button" class="danger-button"
|
||||||
|
(click)="removerMaterialAtividade(i)">
|
||||||
|
Remover
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="empty-subsection" *ngIf="materiaisAtividade.length === 0">
|
||||||
|
Nenhum material adicionado nesta atividade.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="inline-form-actions">
|
||||||
|
<button mat-button type="button" (click)="cancelarEdicaoAtividade()">Cancelar</button>
|
||||||
|
<button mat-raised-button color="primary" type="button"
|
||||||
|
(click)="salvarAtividade()" [disabled]="salvandoAtividade">
|
||||||
|
{{ salvandoAtividade ? 'Salvando...' : 'Salvar atividade' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="activity-card" *ngFor="let atividade of atividadesOrdenadas">
|
||||||
|
<div class="activity-header">
|
||||||
|
<div>
|
||||||
|
<span class="activity-date">{{ atividade.data_atividade | date:'dd/MM/yyyy' }}</span>
|
||||||
|
<h3>{{ atividade.descricao }}</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="activity-actions">
|
||||||
|
<button mat-button type="button" (click)="editarAtividade(atividade)">Editar</button>
|
||||||
|
<button mat-button type="button" class="danger-button"
|
||||||
|
(click)="excluirAtividade(atividade)">Excluir</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="activity-meta-grid">
|
||||||
|
<div>
|
||||||
|
<span>Técnicos</span>
|
||||||
|
<strong>{{ nomeTecnicosAtividade(atividade) }}</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Horas</span>
|
||||||
|
<strong>{{ totalHorasAtividade(atividade) | number:'1.2-2' }}</strong>
|
||||||
|
<small>{{ atividade.horas_expediente | number:'1.2-2' }} exp. · {{ atividade.horas_extras | number:'1.2-2' }} extra</small>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Mão de obra</span>
|
||||||
|
<strong>{{ atividade.valor_mao_obra | currency:'BRL':'symbol':'1.2-2' }}</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Materiais</span>
|
||||||
|
<strong>{{ valorMateriaisAtividade(atividade) | currency:'BRL':'symbol':'1.2-2' }}</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="materials-list" *ngIf="atividade.materiais && atividade.materiais.length > 0">
|
||||||
|
<div class="material-chip" *ngFor="let material of atividade.materiais">
|
||||||
|
{{ material.quantidade | number:'1.0-3' }} {{ material.unidade_snapshot }} · {{ material.descricao_snapshot }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="empty-state" *ngIf="atividadesOrdenadas.length === 0 && !exibirFormAtividade">
|
||||||
|
<strong>Nenhuma atividade registrada.</strong>
|
||||||
|
<p>Adicione o primeiro serviço realizado nesta manutenção.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
|
||||||
|
<mat-tab label="Financeiro">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="financial-grid">
|
||||||
|
<article><span>Mão de obra</span><strong>{{ ordem.valor_mao_obra | currency:'BRL':'symbol':'1.2-2' }}</strong></article>
|
||||||
|
<article><span>Insumos</span><strong>{{ ordem.valor_insumos | currency:'BRL':'symbol':'1.2-2' }}</strong></article>
|
||||||
|
<article><span>Desconto</span><strong>{{ ordem.valor_desconto | currency:'BRL':'symbol':'1.2-2' }}</strong></article>
|
||||||
|
<article><span>Acréscimo</span><strong>{{ ordem.valor_acrescimo | currency:'BRL':'symbol':'1.2-2' }}</strong></article>
|
||||||
|
<article class="financial-total"><span>Valor total</span><strong>{{ ordem.valor_total | currency:'BRL':'symbol':'1.2-2' }}</strong></article>
|
||||||
|
<article><span>Pago</span><strong>{{ ordem.valor_pago | currency:'BRL':'symbol':'1.2-2' }}</strong></article>
|
||||||
|
<article><span>Pendente</span><strong>{{ valorPendente | currency:'BRL':'symbol':'1.2-2' }}</strong></article>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-toolbar">
|
||||||
|
<div>
|
||||||
|
<h2>Pagamentos</h2>
|
||||||
|
<p>O status financeiro é calculado pelos pagamentos registrados.</p>
|
||||||
|
</div>
|
||||||
|
<button mat-raised-button color="primary" type="button" (click)="exibirFormPagamento = true" *ngIf="!exibirFormPagamento || (formOrdem.get('tipo').value !== 'CORTESIA' && formOrdem.get('tipo').value !== 'GARANTIA')">
|
||||||
|
Registrar pagamento
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="inline-form-card" [formGroup]="formPagamento" *ngIf="exibirFormPagamento">
|
||||||
|
<div class="form-grid form-grid-4">
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Data e hora</mat-label>
|
||||||
|
<input matInput type="datetime-local" formControlName="data_pagamento">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Valor</mat-label>
|
||||||
|
<input matInput type="number" min="0.01" step="0.01" formControlName="valor">
|
||||||
|
<span matPrefix>R$ </span>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Forma</mat-label>
|
||||||
|
<mat-select formControlName="forma_pagamento">
|
||||||
|
<mat-option *ngFor="let opcao of formaPagamentoOpcoes" [value]="opcao.valor">
|
||||||
|
{{ opcao.texto }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Referência</mat-label>
|
||||||
|
<input matInput formControlName="referencia">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="span-4">
|
||||||
|
<mat-label>Observações</mat-label>
|
||||||
|
<textarea matInput rows="2" formControlName="observacoes"></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="inline-form-actions">
|
||||||
|
<button mat-button type="button" (click)="exibirFormPagamento = false">Cancelar</button>
|
||||||
|
<button mat-raised-button color="primary" type="button" (click)="registrarPagamento()" [disabled]="salvandoPagamento">
|
||||||
|
{{ salvandoPagamento ? 'Registrando...' : 'Registrar pagamento' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="simple-list-item" *ngFor="let pagamento of ordem.pagamentos">
|
||||||
|
<div>
|
||||||
|
<strong>{{ pagamento.valor | currency:'BRL':'symbol':'1.2-2' }}</strong>
|
||||||
|
<span>{{ pagamento.data_pagamento | date:'dd/MM/yyyy HH:mm' }} · {{ pagamento.forma_pagamento }}</span>
|
||||||
|
<small *ngIf="pagamento.referencia">{{ pagamento.referencia }}</small>
|
||||||
|
</div>
|
||||||
|
<button mat-button type="button" class="danger-button" *ngIf="!pagamento.cancelado_em" (click)="cancelarPagamento(pagamento)">
|
||||||
|
Cancelar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="empty-state" *ngIf="(!ordem.pagamentos || ordem.pagamentos.length === 0) && formOrdem.get('tipo').value != 'CORTESIA' && formOrdem.get('tipo').value != 'GARANTIA'">
|
||||||
|
<strong>Nenhum pagamento registrado.</strong>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="billing-notice"
|
||||||
|
*ngIf="formOrdem.get('tipo').value === 'CORTESIA'">
|
||||||
|
|
||||||
|
Esta OS é uma cortesia. Não é necessário registrar pagamento.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="billing-notice"
|
||||||
|
*ngIf="formOrdem.get('tipo').value === 'GARANTIA'">
|
||||||
|
|
||||||
|
Esta OS está em garantia. As horas e os materiais continuam
|
||||||
|
registrados para controle interno, mas não são cobrados.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
|
||||||
|
<mat-tab label="Odômetros">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-toolbar">
|
||||||
|
<div>
|
||||||
|
<h2>Histórico de odômetros</h2>
|
||||||
|
<p>Leituras registradas na entrada, durante os testes e na saída.</p>
|
||||||
|
</div>
|
||||||
|
<button mat-raised-button color="primary" type="button"
|
||||||
|
(click)="exibirFormOdometro = true" *ngIf="!exibirFormOdometro">
|
||||||
|
Nova leitura
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="inline-form-card" [formGroup]="formOdometro" *ngIf="exibirFormOdometro">
|
||||||
|
<div class="form-grid form-grid-4">
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Tipo de leitura</mat-label>
|
||||||
|
<mat-select formControlName="tipo_leitura">
|
||||||
|
<mat-option *ngFor="let opcao of tipoLeituraOpcoes" [value]="opcao.valor">
|
||||||
|
{{ opcao.texto }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Data e hora</mat-label>
|
||||||
|
<input matInput type="datetime-local" formControlName="leitura_em">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Total em segundos</mat-label>
|
||||||
|
<input matInput type="number" min="0" formControlName="odometro_total_segundos">
|
||||||
|
<mat-hint>{{ segundosParaTempo(formOdometro.get('odometro_total_segundos').value) }}</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Conectado em segundos</mat-label>
|
||||||
|
<input matInput type="number" min="0" formControlName="odometro_conectado_segundos">
|
||||||
|
<mat-hint>{{ segundosParaTempo(formOdometro.get('odometro_conectado_segundos').value) }}</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Movimento em segundos</mat-label>
|
||||||
|
<input matInput type="number" min="0" formControlName="odometro_movimento_segundos">
|
||||||
|
<mat-hint>{{ segundosParaTempo(formOdometro.get('odometro_movimento_segundos').value) }}</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Parcial em segundos</mat-label>
|
||||||
|
<input matInput type="number" min="0" formControlName="odometro_parcial_segundos">
|
||||||
|
<mat-hint>{{ segundosParaTempo(formOdometro.get('odometro_parcial_segundos').value) }}</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="span-2">
|
||||||
|
<mat-label>Observações</mat-label>
|
||||||
|
<input matInput formControlName="observacoes">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="inline-form-actions">
|
||||||
|
<button mat-button type="button" (click)="exibirFormOdometro = false">Cancelar</button>
|
||||||
|
<button mat-raised-button color="primary" type="button"
|
||||||
|
(click)="registrarOdometro()" [disabled]="salvandoOdometro">
|
||||||
|
{{ salvandoOdometro ? 'Registrando...' : 'Registrar leitura' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="odometer-card" *ngFor="let leitura of ordem.odometros_da_os">
|
||||||
|
<div class="odometer-header">
|
||||||
|
<div>
|
||||||
|
<strong>{{ leitura.tipo_leitura }}</strong>
|
||||||
|
<span>{{ leitura.leitura_em | date:'dd/MM/yyyy HH:mm' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="odometer-grid">
|
||||||
|
<div><span>Total</span><strong>{{ segundosParaTempo(leitura.odometro_total_segundos) }}</strong></div>
|
||||||
|
<div><span>Conectado</span><strong>{{ segundosParaTempo(leitura.odometro_conectado_segundos) }}</strong></div>
|
||||||
|
<div><span>Movimento</span><strong>{{ segundosParaTempo(leitura.odometro_movimento_segundos) }}</strong></div>
|
||||||
|
<div><span>Parcial</span><strong>{{ segundosParaTempo(leitura.odometro_parcial_segundos) }}</strong></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p *ngIf="leitura.observacoes">{{ leitura.observacoes }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="empty-state" *ngIf="!ordem.odometros_da_os || ordem.odometros_da_os.length === 0">
|
||||||
|
<strong>Nenhuma leitura vinculada a esta OS.</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
|
||||||
|
<mat-tab label="Histórico">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-toolbar">
|
||||||
|
<div>
|
||||||
|
<h2>Histórico de status</h2>
|
||||||
|
<p>Alterações registradas durante a vida da OS.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="timeline-item" *ngFor="let item of ordem.historico_status">
|
||||||
|
<div class="timeline-dot"></div>
|
||||||
|
<div>
|
||||||
|
<strong>{{ labelStatus(item.status_novo) }}</strong>
|
||||||
|
<span>{{ item.created_at | date:'dd/MM/yyyy HH:mm' }}</span>
|
||||||
|
<p *ngIf="item.observacoes">{{ item.observacoes }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="empty-state" *ngIf="!ordem.historico_status || ordem.historico_status.length === 0">
|
||||||
|
<strong>Nenhuma alteração de status registrada.</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,589 @@
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f4f6f8;
|
||||||
|
color: #1f2933;
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-form-page {
|
||||||
|
max-width: 1680px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 24px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header,
|
||||||
|
.header-main,
|
||||||
|
.header-actions,
|
||||||
|
.activity-actions,
|
||||||
|
.inline-form-actions,
|
||||||
|
.section-heading,
|
||||||
|
.inline-form-header,
|
||||||
|
.subsection-heading,
|
||||||
|
.tab-toolbar,
|
||||||
|
.activity-header,
|
||||||
|
.odometer-header {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header,
|
||||||
|
.section-heading,
|
||||||
|
.inline-form-header,
|
||||||
|
.subsection-heading,
|
||||||
|
.tab-toolbar,
|
||||||
|
.activity-header,
|
||||||
|
.odometer-header {
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-main,
|
||||||
|
.header-actions,
|
||||||
|
.activity-actions,
|
||||||
|
.inline-form-actions {
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-main {
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 4px 0 5px;
|
||||||
|
color: #101828;
|
||||||
|
font-size: 30px;
|
||||||
|
line-height: 1.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
color: #667085;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow,
|
||||||
|
.section-kicker {
|
||||||
|
color: #1769aa;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 1.1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-card,
|
||||||
|
.inline-form-card,
|
||||||
|
.activity-card,
|
||||||
|
.loading-card,
|
||||||
|
.odometer-card,
|
||||||
|
.simple-list-item,
|
||||||
|
.os-tabs {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e4e7ec;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(16, 24, 40, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-card {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-heading,
|
||||||
|
.inline-form-header,
|
||||||
|
.subsection-heading {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-heading h2,
|
||||||
|
.tab-toolbar h2,
|
||||||
|
.inline-form-header h3,
|
||||||
|
.subsection-heading h4 {
|
||||||
|
margin: 4px 0 0;
|
||||||
|
color: #101828;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-heading p,
|
||||||
|
.tab-toolbar p,
|
||||||
|
.subsection-heading p {
|
||||||
|
margin: 5px 0 0;
|
||||||
|
color: #667085;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grid-4 {
|
||||||
|
grid-template-columns: repeat(4, minmax(160px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.span-2 { grid-column: span 2; }
|
||||||
|
.span-3 { grid-column: span 3; }
|
||||||
|
.span-4 { grid-column: span 4; }
|
||||||
|
|
||||||
|
.form-grid .mat-form-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-tabs {
|
||||||
|
display: block;
|
||||||
|
margin-top: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-toolbar {
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-form-card {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
padding: 18px;
|
||||||
|
background: #fbfcfe;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-form-actions {
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-field {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
min-height: 38px;
|
||||||
|
color: #344054;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-checkbox {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.materials-section {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding-top: 16px;
|
||||||
|
border-top: 1px solid #eaecf0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns:
|
||||||
|
minmax(170px, 1.1fr)
|
||||||
|
minmax(220px, 1.8fr)
|
||||||
|
90px
|
||||||
|
110px
|
||||||
|
140px
|
||||||
|
140px
|
||||||
|
90px
|
||||||
|
auto;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: start;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 12px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #eaecf0;
|
||||||
|
border-radius: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-subsection,
|
||||||
|
.empty-state {
|
||||||
|
padding: 28px;
|
||||||
|
color: #667085;
|
||||||
|
text-align: center;
|
||||||
|
background: #f8fafc;
|
||||||
|
border: 1px dashed #d0d5dd;
|
||||||
|
border-radius: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
strong {
|
||||||
|
display: block;
|
||||||
|
color: #344054;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 5px 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-card {
|
||||||
|
margin-bottom: 14px;
|
||||||
|
padding: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-date {
|
||||||
|
color: #1769aa;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-header h3 {
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 6px 0 0;
|
||||||
|
color: #344054;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 1.55;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-meta-grid,
|
||||||
|
.financial-grid,
|
||||||
|
.odometer-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-meta-grid {
|
||||||
|
grid-template-columns: 2fr repeat(3, minmax(140px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-meta-grid > div,
|
||||||
|
.odometer-grid > div {
|
||||||
|
padding: 12px;
|
||||||
|
background: #f8fafc;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-meta-grid span,
|
||||||
|
.odometer-grid span,
|
||||||
|
.financial-grid span {
|
||||||
|
display: block;
|
||||||
|
color: #667085;
|
||||||
|
font-size: 11px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-meta-grid strong,
|
||||||
|
.odometer-grid strong {
|
||||||
|
display: block;
|
||||||
|
margin-top: 5px;
|
||||||
|
color: #344054;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-meta-grid small {
|
||||||
|
display: block;
|
||||||
|
margin-top: 4px;
|
||||||
|
color: #98a2b3;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.materials-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 7px;
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-chip {
|
||||||
|
padding: 5px 9px;
|
||||||
|
background: #eff8ff;
|
||||||
|
color: #175cd3;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.financial-grid {
|
||||||
|
grid-template-columns: repeat(7, minmax(140px, 1fr));
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.financial-grid article {
|
||||||
|
padding: 16px;
|
||||||
|
background: #f8fafc;
|
||||||
|
border-radius: 9px;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
display: block;
|
||||||
|
margin-top: 7px;
|
||||||
|
color: #101828;
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.financial-grid .financial-total {
|
||||||
|
background: #eef6ff;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: #175cd3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-list-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 18px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
|
||||||
|
strong,
|
||||||
|
span,
|
||||||
|
small {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong { color: #344054; }
|
||||||
|
|
||||||
|
span,
|
||||||
|
small {
|
||||||
|
margin-top: 3px;
|
||||||
|
color: #667085;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.odometer-card {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.odometer-header {
|
||||||
|
strong,
|
||||||
|
span { display: block; }
|
||||||
|
strong { color: #344054; }
|
||||||
|
span {
|
||||||
|
margin-top: 3px;
|
||||||
|
color: #667085;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.odometer-grid {
|
||||||
|
grid-template-columns: repeat(4, minmax(140px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.odometer-card p {
|
||||||
|
margin: 12px 0 0;
|
||||||
|
color: #667085;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-item {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 18px 1fr;
|
||||||
|
gap: 12px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 14px;
|
||||||
|
bottom: -2px;
|
||||||
|
left: 6px;
|
||||||
|
width: 2px;
|
||||||
|
background: #e4e7ec;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child::before { display: none; }
|
||||||
|
|
||||||
|
strong,
|
||||||
|
span { display: block; }
|
||||||
|
strong { color: #344054; }
|
||||||
|
span {
|
||||||
|
margin-top: 3px;
|
||||||
|
color: #667085;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin: 7px 0 0;
|
||||||
|
color: #475467;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-dot {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
margin-top: 3px;
|
||||||
|
background: #1769aa;
|
||||||
|
border: 3px solid #eaf4ff;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 24px;
|
||||||
|
padding: 3px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-sucesso { color: #027a48; background: #ecfdf3; }
|
||||||
|
.badge-alerta { color: #b54708; background: #fffaeb; }
|
||||||
|
.badge-erro { color: #b42318; background: #fef3f2; }
|
||||||
|
.badge-info { color: #175cd3; background: #eff8ff; }
|
||||||
|
.badge-neutro { color: #475467; background: #f2f4f7; }
|
||||||
|
|
||||||
|
.message {
|
||||||
|
margin-bottom: 14px;
|
||||||
|
padding: 12px 15px;
|
||||||
|
border-radius: 9px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-error {
|
||||||
|
color: #b42318;
|
||||||
|
background: #fef3f2;
|
||||||
|
border: 1px solid #fecdca;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-success {
|
||||||
|
color: #027a48;
|
||||||
|
background: #ecfdf3;
|
||||||
|
border: 1px solid #abefc6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-card {
|
||||||
|
min-height: 220px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #667085;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-bar {
|
||||||
|
width: 170px;
|
||||||
|
height: 4px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: linear-gradient(90deg, #d0d5dd 0%, #1769aa 45%, #d0d5dd 100%);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: loading 1.2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.danger-button { color: #b42318 !important; }
|
||||||
|
|
||||||
|
@keyframes loading {
|
||||||
|
from { background-position: 200% 0; }
|
||||||
|
to { background-position: -200% 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Blindagem contra o tema global antigo */
|
||||||
|
:host ::ng-deep .os-form-page .mat-form-field,
|
||||||
|
:host ::ng-deep .os-form-page .mat-input-element,
|
||||||
|
:host ::ng-deep .os-form-page .mat-select-value,
|
||||||
|
:host ::ng-deep .os-form-page .mat-select-value-text {
|
||||||
|
color: #101828 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-form-page .mat-form-field-label,
|
||||||
|
:host ::ng-deep .os-form-page .mat-select-arrow,
|
||||||
|
:host ::ng-deep .os-form-page .mat-hint {
|
||||||
|
color: #667085 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-form-page .mat-form-field-outline {
|
||||||
|
color: #d0d5dd !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-form-page .mat-form-field-outline-thick {
|
||||||
|
color: #1769aa !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-form-page .mat-form-field-flex {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-form-page .mat-tab-label {
|
||||||
|
color: #344054 !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-form-page .mat-tab-label-active {
|
||||||
|
color: #1769aa !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-form-page .mat-tab-header {
|
||||||
|
background: #f8fafc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1350px) {
|
||||||
|
.form-grid-4 { grid-template-columns: repeat(2, minmax(180px, 1fr)); }
|
||||||
|
.span-3,
|
||||||
|
.span-4 { grid-column: span 2; }
|
||||||
|
.material-row { grid-template-columns: repeat(3, minmax(160px, 1fr)); }
|
||||||
|
.financial-grid { grid-template-columns: repeat(4, minmax(150px, 1fr)); }
|
||||||
|
.activity-meta-grid { grid-template-columns: repeat(2, minmax(180px, 1fr)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 760px) {
|
||||||
|
.os-form-page { padding: 14px; }
|
||||||
|
|
||||||
|
.page-header,
|
||||||
|
.header-main,
|
||||||
|
.header-actions,
|
||||||
|
.section-heading,
|
||||||
|
.tab-toolbar,
|
||||||
|
.activity-header,
|
||||||
|
.subsection-heading {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
align-items: stretch;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
button { width: 100%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grid-4,
|
||||||
|
.material-row,
|
||||||
|
.financial-grid,
|
||||||
|
.activity-meta-grid,
|
||||||
|
.odometer-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.span-2,
|
||||||
|
.span-3,
|
||||||
|
.span-4 {
|
||||||
|
grid-column: span 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.billing-notice {
|
||||||
|
grid-column: span 4;
|
||||||
|
padding: 12px 14px;
|
||||||
|
color: #175cd3;
|
||||||
|
background: #eff8ff;
|
||||||
|
border: 1px solid #b2ddff;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { OrionOrdemServicoFormComponent } from './orion-ordem-servico-form.component';
|
||||||
|
|
||||||
|
describe('OrionOrdemServicoFormComponent', () => {
|
||||||
|
let component: OrionOrdemServicoFormComponent;
|
||||||
|
let fixture: ComponentFixture<OrionOrdemServicoFormComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ OrionOrdemServicoFormComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(OrionOrdemServicoFormComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,911 @@
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import {
|
||||||
|
AbstractControl,
|
||||||
|
FormArray,
|
||||||
|
FormBuilder,
|
||||||
|
FormGroup,
|
||||||
|
Validators
|
||||||
|
} from '@angular/forms';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ClienteManutencaoModel,
|
||||||
|
EquipamentoManutencaoModel,
|
||||||
|
MaterialManutencaoModel,
|
||||||
|
OrdemServicoAtividadeMaterialModel,
|
||||||
|
OrdemServicoAtividadeModel,
|
||||||
|
OrdemServicoAtividadeSalvarModel,
|
||||||
|
OrdemServicoAtividadeTecnicoModel,
|
||||||
|
OrdemServicoDetalhesModel,
|
||||||
|
OrdemServicoOdometroModel,
|
||||||
|
OrdemServicoPagamentoModel,
|
||||||
|
OrdemServicoSalvarModel,
|
||||||
|
OrdemServicoStatus,
|
||||||
|
OrdemServicoStatusPagamento,
|
||||||
|
OrdemServicoTipo,
|
||||||
|
TecnicoManutencaoModel
|
||||||
|
} from '../../models/orionModel';
|
||||||
|
|
||||||
|
import { OrionService } from '../../services/orionService';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-orion-ordem-servico-form',
|
||||||
|
templateUrl: './orion-ordem-servico-form.component.html',
|
||||||
|
styleUrls: ['./orion-ordem-servico-form.component.scss']
|
||||||
|
})
|
||||||
|
export class OrionOrdemServicoFormComponent implements OnInit {
|
||||||
|
|
||||||
|
modoEdicao = false;
|
||||||
|
ordemServicoId: number = null;
|
||||||
|
ordem: OrdemServicoDetalhesModel = null;
|
||||||
|
|
||||||
|
carregando = true;
|
||||||
|
salvando = false;
|
||||||
|
salvandoAtividade = false;
|
||||||
|
salvandoPagamento = false;
|
||||||
|
salvandoOdometro = false;
|
||||||
|
|
||||||
|
erro = '';
|
||||||
|
sucesso = '';
|
||||||
|
|
||||||
|
formOrdem: FormGroup;
|
||||||
|
formOdometroEntrada: FormGroup;
|
||||||
|
formAtividade: FormGroup;
|
||||||
|
formPagamento: FormGroup;
|
||||||
|
formOdometro: FormGroup;
|
||||||
|
|
||||||
|
clientes: ClienteManutencaoModel[] = [];
|
||||||
|
equipamentos: EquipamentoManutencaoModel[] = [];
|
||||||
|
tecnicos: TecnicoManutencaoModel[] = [];
|
||||||
|
materiais: MaterialManutencaoModel[] = [];
|
||||||
|
|
||||||
|
exibirFormAtividade = false;
|
||||||
|
atividadeEditandoId: number = null;
|
||||||
|
exibirFormPagamento = false;
|
||||||
|
exibirFormOdometro = false;
|
||||||
|
|
||||||
|
statusOpcoes = [
|
||||||
|
{ valor: 'RASCUNHO', texto: 'Rascunho' },
|
||||||
|
{ valor: 'ABERTA', texto: 'Aberta' },
|
||||||
|
{ valor: 'EM_DIAGNOSTICO', texto: 'Em diagnóstico' },
|
||||||
|
{ valor: 'AGUARDANDO_APROVACAO', texto: 'Aguardando aprovação' },
|
||||||
|
{ valor: 'AGUARDANDO_PECA', texto: 'Aguardando peça' },
|
||||||
|
{ valor: 'EM_EXECUCAO', texto: 'Em execução' },
|
||||||
|
{ valor: 'EM_TESTES', texto: 'Em testes' },
|
||||||
|
{ valor: 'FINALIZADA', texto: 'Finalizada' },
|
||||||
|
{ valor: 'AGUARDANDO_RETIRADA', texto: 'Aguardando retirada' },
|
||||||
|
{ valor: 'RETIRADA', texto: 'Retirada' },
|
||||||
|
{ valor: 'CANCELADA', texto: 'Cancelada' }
|
||||||
|
];
|
||||||
|
|
||||||
|
tipoOpcoes = [
|
||||||
|
{ valor: 'CORRETIVA', texto: 'Corretiva' },
|
||||||
|
{ valor: 'PREVENTIVA', texto: 'Preventiva' },
|
||||||
|
{ valor: 'REFORMA', texto: 'Reforma' },
|
||||||
|
{ valor: 'DESENVOLVIMENTO', texto: 'Desenvolvimento' },
|
||||||
|
{ valor: 'FABRICACAO', texto: 'Fabricação' },
|
||||||
|
{ valor: 'GARANTIA', texto: 'Garantia' },
|
||||||
|
{ valor: 'CORTESIA', texto: 'Cortesia' },
|
||||||
|
{ valor: 'INTERNA', texto: 'Interna' },
|
||||||
|
{ valor: 'OUTRO', texto: 'Outro' }
|
||||||
|
];
|
||||||
|
|
||||||
|
prioridadeOpcoes = [
|
||||||
|
{ valor: 'BAIXA', texto: 'Baixa' },
|
||||||
|
{ valor: 'NORMAL', texto: 'Normal' },
|
||||||
|
{ valor: 'ALTA', texto: 'Alta' },
|
||||||
|
{ valor: 'URGENTE', texto: 'Urgente' }
|
||||||
|
];
|
||||||
|
|
||||||
|
formaPagamentoOpcoes = [
|
||||||
|
{ valor: 'DINHEIRO', texto: 'Dinheiro' },
|
||||||
|
{ valor: 'PIX', texto: 'PIX' },
|
||||||
|
{ valor: 'TRANSFERENCIA', texto: 'Transferência' },
|
||||||
|
{ valor: 'BOLETO', texto: 'Boleto' },
|
||||||
|
{ valor: 'CARTAO', texto: 'Cartão' },
|
||||||
|
{ valor: 'OUTRO', texto: 'Outro' }
|
||||||
|
];
|
||||||
|
|
||||||
|
tipoLeituraOpcoes = [
|
||||||
|
{ valor: 'SAIDA_MANUTENCAO', texto: 'Saída da manutenção' },
|
||||||
|
{ valor: 'ATUALIZACAO_MANUAL', texto: 'Atualização manual' },
|
||||||
|
{ valor: 'TELEMETRIA', texto: 'Telemetria' }
|
||||||
|
];
|
||||||
|
|
||||||
|
baixandoPdf: boolean = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
private orionService: OrionService
|
||||||
|
) {
|
||||||
|
this.criarFormularios();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
var idParam = this.route.snapshot.paramMap.get('id');
|
||||||
|
var id = Number(idParam);
|
||||||
|
|
||||||
|
this.modoEdicao = !!idParam && !isNaN(id) && id > 0;
|
||||||
|
this.ordemServicoId = this.modoEdicao ? id : null;
|
||||||
|
|
||||||
|
this.carregarDadosIniciais();
|
||||||
|
}
|
||||||
|
|
||||||
|
tipoOrdemAlterado(tipo: OrdemServicoTipo) {
|
||||||
|
var statusPagamentoAtual =
|
||||||
|
this.formOrdem.get('status_pagamento').value;
|
||||||
|
|
||||||
|
if (tipo === 'CORTESIA') {
|
||||||
|
this.formOrdem.patchValue({
|
||||||
|
status_pagamento: 'CORTESIA'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.formAtividade.patchValue({
|
||||||
|
cobravel: false
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tipo === 'GARANTIA') {
|
||||||
|
this.formOrdem.patchValue({
|
||||||
|
status_pagamento: 'NAO_APLICAVEL'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.formAtividade.patchValue({
|
||||||
|
cobravel: false
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
statusPagamentoAtual === 'CORTESIA' ||
|
||||||
|
statusPagamentoAtual === 'NAO_APLICAVEL'
|
||||||
|
) {
|
||||||
|
this.formOrdem.patchValue({
|
||||||
|
status_pagamento: 'PENDENTE'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private criarFormularios() {
|
||||||
|
this.formOrdem = this.formBuilder.group({
|
||||||
|
cliente_id: [null],
|
||||||
|
equipamento_id: [null, Validators.required],
|
||||||
|
tecnico_responsavel_id: [null],
|
||||||
|
tipo: ['CORRETIVA', Validators.required],
|
||||||
|
status: ['ABERTA', Validators.required],
|
||||||
|
status_pagamento: ['PENDENTE'],
|
||||||
|
prioridade: ['NORMAL', Validators.required],
|
||||||
|
data_entrada: [this.formatarDataHoraLocal(new Date()), Validators.required],
|
||||||
|
data_previsao: [''],
|
||||||
|
data_finalizacao: [''],
|
||||||
|
data_retirada: [''],
|
||||||
|
problema_relatado: ['', Validators.required],
|
||||||
|
diagnostico: [''],
|
||||||
|
solucao_resumo: [''],
|
||||||
|
responsavel_entrega: [''],
|
||||||
|
responsavel_retirada: [''],
|
||||||
|
observacoes_internas: [''],
|
||||||
|
observacoes_cliente: [''],
|
||||||
|
garantia_dias: [null],
|
||||||
|
garantia_ate: [''],
|
||||||
|
valor_desconto: [0],
|
||||||
|
valor_acrescimo: [0],
|
||||||
|
justificativa_ajuste_valor: [''],
|
||||||
|
observacao_status: ['']
|
||||||
|
});
|
||||||
|
|
||||||
|
this.formOdometroEntrada = this.formBuilder.group({
|
||||||
|
odometro_total_segundos: [0, [Validators.required, Validators.min(0)]],
|
||||||
|
odometro_conectado_segundos: [0, [Validators.required, Validators.min(0)]],
|
||||||
|
odometro_movimento_segundos: [0, [Validators.required, Validators.min(0)]],
|
||||||
|
odometro_parcial_segundos: [0, [Validators.required, Validators.min(0)]],
|
||||||
|
leitura_em: [this.formatarDataHoraLocal(new Date())],
|
||||||
|
observacoes: ['']
|
||||||
|
});
|
||||||
|
|
||||||
|
this.formAtividade = this.formBuilder.group({
|
||||||
|
data_atividade: [this.formatarData(new Date()), Validators.required],
|
||||||
|
descricao: ['', Validators.required],
|
||||||
|
ordem_exibicao: [0],
|
||||||
|
horas_expediente: [0, [Validators.min(0)]],
|
||||||
|
horas_extras: [0, [Validators.min(0)]],
|
||||||
|
valor_hora_expediente: [60, [Validators.min(0)]],
|
||||||
|
valor_hora_extra: [90, [Validators.min(0)]],
|
||||||
|
cobravel: [true],
|
||||||
|
observacoes_internas: [''],
|
||||||
|
observacoes_cliente: [''],
|
||||||
|
tecnico_ids: [[]],
|
||||||
|
materiais: this.formBuilder.array([])
|
||||||
|
});
|
||||||
|
|
||||||
|
this.formPagamento = this.formBuilder.group({
|
||||||
|
data_pagamento: [this.formatarDataHoraLocal(new Date()), Validators.required],
|
||||||
|
valor: [0, [Validators.required, Validators.min(0.01)]],
|
||||||
|
forma_pagamento: ['PIX', Validators.required],
|
||||||
|
referencia: [''],
|
||||||
|
observacoes: ['']
|
||||||
|
});
|
||||||
|
|
||||||
|
this.formOdometro = this.formBuilder.group({
|
||||||
|
tipo_leitura: ['SAIDA_MANUTENCAO', Validators.required],
|
||||||
|
leitura_em: [this.formatarDataHoraLocal(new Date()), Validators.required],
|
||||||
|
odometro_total_segundos: [0, [Validators.required, Validators.min(0)]],
|
||||||
|
odometro_conectado_segundos: [0, [Validators.required, Validators.min(0)]],
|
||||||
|
odometro_movimento_segundos: [0, [Validators.required, Validators.min(0)]],
|
||||||
|
odometro_parcial_segundos: [0, [Validators.required, Validators.min(0)]],
|
||||||
|
observacoes: ['']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private carregarDadosIniciais() {
|
||||||
|
this.carregando = true;
|
||||||
|
this.erro = '';
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
this.orionService.getClientesManutencao(),
|
||||||
|
this.orionService.getEquipamentosManutencao(),
|
||||||
|
this.orionService.getTecnicosManutencao(),
|
||||||
|
this.orionService.getMateriaisManutencao()
|
||||||
|
])
|
||||||
|
.then(resultados => {
|
||||||
|
this.clientes = resultados[0] || [];
|
||||||
|
this.equipamentos = resultados[1] || [];
|
||||||
|
this.tecnicos = resultados[2] || [];
|
||||||
|
this.materiais = resultados[3] || [];
|
||||||
|
|
||||||
|
if (this.modoEdicao) {
|
||||||
|
return this.carregarOrdem();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('[OS FORM] Erro ao carregar dados:', error);
|
||||||
|
this.erro = this.obterMensagemErro(error, 'Não foi possível carregar os dados necessários.');
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.carregando = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private carregarOrdem(): Promise<any> {
|
||||||
|
return this.orionService.getOrdemServico(this.ordemServicoId)
|
||||||
|
.then(ordem => {
|
||||||
|
this.ordem = ordem;
|
||||||
|
this.preencherFormularioOrdem(ordem);
|
||||||
|
this.preencherNovoOdometroComUltimaLeitura();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private preencherFormularioOrdem(ordem: OrdemServicoDetalhesModel) {
|
||||||
|
this.formOrdem.patchValue({
|
||||||
|
cliente_id: ordem.cliente_id || null,
|
||||||
|
equipamento_id: ordem.equipamento_id || null,
|
||||||
|
tecnico_responsavel_id: ordem.tecnico_responsavel_id || null,
|
||||||
|
tipo: ordem.tipo || 'CORRETIVA',
|
||||||
|
status: ordem.status || 'ABERTA',
|
||||||
|
status_pagamento: ordem.status_pagamento || 'PENDENTE',
|
||||||
|
prioridade: ordem.prioridade || 'NORMAL',
|
||||||
|
data_entrada: this.converterDataApiParaLocal(ordem.data_entrada),
|
||||||
|
data_previsao: this.converterDataApiParaLocal(ordem.data_previsao),
|
||||||
|
data_finalizacao: this.converterDataApiParaLocal(ordem.data_finalizacao),
|
||||||
|
data_retirada: this.converterDataApiParaLocal(ordem.data_retirada),
|
||||||
|
problema_relatado: ordem.problema_relatado || '',
|
||||||
|
diagnostico: ordem.diagnostico || '',
|
||||||
|
solucao_resumo: ordem.solucao_resumo || '',
|
||||||
|
responsavel_entrega: ordem.responsavel_entrega || '',
|
||||||
|
responsavel_retirada: ordem.responsavel_retirada || '',
|
||||||
|
observacoes_internas: ordem.observacoes_internas || '',
|
||||||
|
observacoes_cliente: ordem.observacoes_cliente || '',
|
||||||
|
garantia_dias: ordem.garantia_dias || null,
|
||||||
|
garantia_ate: ordem.garantia_ate ? String(ordem.garantia_ate).substring(0, 10) : '',
|
||||||
|
valor_desconto: this.numero(ordem.valor_desconto),
|
||||||
|
valor_acrescimo: this.numero(ordem.valor_acrescimo),
|
||||||
|
justificativa_ajuste_valor: ordem.justificativa_ajuste_valor || '',
|
||||||
|
observacao_status: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
equipamentoSelecionado(equipamentoId: number) {
|
||||||
|
if (!equipamentoId || this.modoEdicao) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var equipamento = this.buscarEquipamento(equipamentoId);
|
||||||
|
if (!equipamento) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.formOrdem.patchValue({ cliente_id: equipamento.cliente_id || null });
|
||||||
|
this.formOdometroEntrada.patchValue({
|
||||||
|
odometro_total_segundos: this.numero(equipamento.odometro_total_segundos),
|
||||||
|
odometro_conectado_segundos: this.numero(equipamento.odometro_conectado_segundos),
|
||||||
|
odometro_movimento_segundos: this.numero(equipamento.odometro_movimento_segundos),
|
||||||
|
odometro_parcial_segundos: this.numero(equipamento.odometro_parcial_segundos)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
salvarOrdem() {
|
||||||
|
this.limparMensagens();
|
||||||
|
|
||||||
|
if (this.formOrdem.invalid) {
|
||||||
|
this.marcarFormularioComoTocado(this.formOrdem);
|
||||||
|
this.erro = 'Preencha os campos obrigatórios da ordem de serviço.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.modoEdicao && this.formOdometroEntrada.invalid) {
|
||||||
|
this.marcarFormularioComoTocado(this.formOdometroEntrada);
|
||||||
|
this.erro = 'Confira os odômetros de entrada.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.salvando = true;
|
||||||
|
var payload = this.montarPayloadOrdem();
|
||||||
|
var operacao = this.modoEdicao
|
||||||
|
? this.orionService.updateOrdemServico(this.ordemServicoId, payload)
|
||||||
|
: this.orionService.insertOrdemServico(payload);
|
||||||
|
|
||||||
|
operacao
|
||||||
|
.then(resultado => {
|
||||||
|
if (!this.modoEdicao) {
|
||||||
|
var novoId = resultado.id || resultado.ordem_servico_id;
|
||||||
|
this.router.navigate(['/orion-ordens-servico', novoId, 'editar']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sucesso = 'Ordem de serviço atualizada com sucesso.';
|
||||||
|
return this.recarregarOrdem();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('[OS FORM] Erro ao salvar OS:', error);
|
||||||
|
this.erro = this.obterMensagemErro(error, 'Não foi possível salvar a ordem de serviço.');
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.salvando = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private montarPayloadOrdem(): OrdemServicoSalvarModel {
|
||||||
|
var valor = this.formOrdem.getRawValue();
|
||||||
|
var payload: OrdemServicoSalvarModel = {
|
||||||
|
cliente_id: this.numeroOuNull(valor.cliente_id),
|
||||||
|
equipamento_id: this.numeroOuNull(valor.equipamento_id),
|
||||||
|
tecnico_responsavel_id: this.numeroOuNull(valor.tecnico_responsavel_id),
|
||||||
|
tipo: valor.tipo,
|
||||||
|
status: valor.status,
|
||||||
|
status_pagamento: valor.status_pagamento,
|
||||||
|
prioridade: valor.prioridade,
|
||||||
|
data_entrada: this.normalizarDataHoraApi(valor.data_entrada),
|
||||||
|
data_previsao: this.normalizarDataHoraApi(valor.data_previsao),
|
||||||
|
data_finalizacao: this.normalizarDataHoraApi(valor.data_finalizacao),
|
||||||
|
data_retirada: this.normalizarDataHoraApi(valor.data_retirada),
|
||||||
|
problema_relatado: this.textoOuNull(valor.problema_relatado),
|
||||||
|
diagnostico: this.textoOuNull(valor.diagnostico),
|
||||||
|
solucao_resumo: this.textoOuNull(valor.solucao_resumo),
|
||||||
|
responsavel_entrega: this.textoOuNull(valor.responsavel_entrega),
|
||||||
|
responsavel_retirada: this.textoOuNull(valor.responsavel_retirada),
|
||||||
|
observacoes_internas: this.textoOuNull(valor.observacoes_internas),
|
||||||
|
observacoes_cliente: this.textoOuNull(valor.observacoes_cliente),
|
||||||
|
garantia_dias: this.numeroOuNull(valor.garantia_dias),
|
||||||
|
garantia_ate: valor.garantia_ate || null,
|
||||||
|
valor_desconto: this.numero(valor.valor_desconto),
|
||||||
|
valor_acrescimo: this.numero(valor.valor_acrescimo),
|
||||||
|
justificativa_ajuste_valor: this.textoOuNull(valor.justificativa_ajuste_valor),
|
||||||
|
observacao_status: this.textoOuNull(valor.observacao_status)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!this.modoEdicao) {
|
||||||
|
var odometro = this.formOdometroEntrada.getRawValue();
|
||||||
|
payload.odometro_entrada = {
|
||||||
|
tipo_leitura: 'ENTRADA_MANUTENCAO',
|
||||||
|
leitura_em: this.normalizarDataHoraApi(odometro.leitura_em),
|
||||||
|
odometro_total_segundos: this.numero(odometro.odometro_total_segundos),
|
||||||
|
odometro_conectado_segundos: this.numero(odometro.odometro_conectado_segundos),
|
||||||
|
odometro_movimento_segundos: this.numero(odometro.odometro_movimento_segundos),
|
||||||
|
odometro_parcial_segundos: this.numero(odometro.odometro_parcial_segundos),
|
||||||
|
observacoes: this.textoOuNull(odometro.observacoes)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
abrirNovaAtividade() {
|
||||||
|
this.atividadeEditandoId = null;
|
||||||
|
this.exibirFormAtividade = true;
|
||||||
|
this.resetarFormAtividade();
|
||||||
|
}
|
||||||
|
|
||||||
|
editarAtividade(atividade: OrdemServicoAtividadeModel) {
|
||||||
|
this.atividadeEditandoId = atividade.id;
|
||||||
|
this.exibirFormAtividade = true;
|
||||||
|
|
||||||
|
this.formAtividade.patchValue({
|
||||||
|
data_atividade: atividade.data_atividade ? String(atividade.data_atividade).substring(0, 10) : this.formatarData(new Date()),
|
||||||
|
descricao: atividade.descricao || '',
|
||||||
|
ordem_exibicao: this.numero(atividade.ordem_exibicao),
|
||||||
|
horas_expediente: this.numero(atividade.horas_expediente),
|
||||||
|
horas_extras: this.numero(atividade.horas_extras),
|
||||||
|
valor_hora_expediente: this.numero(atividade.valor_hora_expediente),
|
||||||
|
valor_hora_extra: this.numero(atividade.valor_hora_extra),
|
||||||
|
cobravel: !!atividade.cobravel,
|
||||||
|
observacoes_internas: atividade.observacoes_internas || '',
|
||||||
|
observacoes_cliente: atividade.observacoes_cliente || '',
|
||||||
|
tecnico_ids: (atividade.tecnicos || []).map(tecnico => tecnico.tecnico_id)
|
||||||
|
});
|
||||||
|
|
||||||
|
this.limparMateriaisAtividade();
|
||||||
|
(atividade.materiais || []).forEach(material => this.adicionarMaterialAtividade(material));
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelarEdicaoAtividade() {
|
||||||
|
this.exibirFormAtividade = false;
|
||||||
|
this.atividadeEditandoId = null;
|
||||||
|
this.resetarFormAtividade();
|
||||||
|
}
|
||||||
|
|
||||||
|
salvarAtividade() {
|
||||||
|
this.limparMensagens();
|
||||||
|
|
||||||
|
if (this.formAtividade.invalid) {
|
||||||
|
this.marcarFormularioComoTocado(this.formAtividade);
|
||||||
|
this.erro = 'Preencha a data e a descrição da atividade.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.salvandoAtividade = true;
|
||||||
|
var payload = this.montarPayloadAtividade();
|
||||||
|
var operacao = this.atividadeEditandoId
|
||||||
|
? this.orionService.updateAtividadeOrdemServico(this.atividadeEditandoId, payload)
|
||||||
|
: this.orionService.insertAtividadeOrdemServico(this.ordemServicoId, payload);
|
||||||
|
|
||||||
|
operacao
|
||||||
|
.then(() => {
|
||||||
|
this.sucesso = this.atividadeEditandoId
|
||||||
|
? 'Atividade atualizada com sucesso.'
|
||||||
|
: 'Atividade adicionada com sucesso.';
|
||||||
|
this.cancelarEdicaoAtividade();
|
||||||
|
return this.recarregarOrdem();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.erro = this.obterMensagemErro(error, 'Não foi possível salvar a atividade.');
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.salvandoAtividade = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
excluirAtividade(atividade: OrdemServicoAtividadeModel) {
|
||||||
|
if (!window.confirm('Deseja realmente excluir esta atividade?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.orionService.deleteAtividadeOrdemServico(atividade.id)
|
||||||
|
.then(() => {
|
||||||
|
this.sucesso = 'Atividade excluída com sucesso.';
|
||||||
|
return this.recarregarOrdem();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.erro = this.obterMensagemErro(error, 'Não foi possível excluir a atividade.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private montarPayloadAtividade(): OrdemServicoAtividadeSalvarModel {
|
||||||
|
var valor = this.formAtividade.getRawValue();
|
||||||
|
var tecnicosAtividade: OrdemServicoAtividadeTecnicoModel[] = [];
|
||||||
|
var materiaisAtividade: OrdemServicoAtividadeMaterialModel[] = [];
|
||||||
|
|
||||||
|
(valor.tecnico_ids || []).forEach(tecnicoId => {
|
||||||
|
tecnicosAtividade.push({ tecnico_id: this.numero(tecnicoId), papel: 'EXECUTOR' });
|
||||||
|
});
|
||||||
|
|
||||||
|
(valor.materiais || []).forEach(material => {
|
||||||
|
materiaisAtividade.push({
|
||||||
|
material_id: this.numeroOuNull(material.material_id),
|
||||||
|
descricao_snapshot: material.descricao_snapshot || 'Material não informado',
|
||||||
|
unidade_snapshot: material.unidade_snapshot || 'UN',
|
||||||
|
quantidade: this.numero(material.quantidade),
|
||||||
|
valor_custo_unitario: this.numero(material.valor_custo_unitario),
|
||||||
|
valor_cobrado_unitario: this.numero(material.valor_cobrado_unitario),
|
||||||
|
cobravel: !!material.cobravel,
|
||||||
|
observacoes: this.textoOuNull(material.observacoes)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
data_atividade: valor.data_atividade,
|
||||||
|
descricao: valor.descricao,
|
||||||
|
ordem_exibicao: this.numero(valor.ordem_exibicao),
|
||||||
|
horas_expediente: this.numero(valor.horas_expediente),
|
||||||
|
horas_extras: this.numero(valor.horas_extras),
|
||||||
|
valor_hora_expediente: this.numero(valor.valor_hora_expediente),
|
||||||
|
valor_hora_extra: this.numero(valor.valor_hora_extra),
|
||||||
|
cobravel: !!valor.cobravel,
|
||||||
|
observacoes_internas: this.textoOuNull(valor.observacoes_internas),
|
||||||
|
observacoes_cliente: this.textoOuNull(valor.observacoes_cliente),
|
||||||
|
tecnicos: tecnicosAtividade,
|
||||||
|
materiais: materiaisAtividade
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get materiaisAtividade(): FormArray {
|
||||||
|
return this.formAtividade.get('materiais') as FormArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
adicionarMaterialAtividade(materialExistente?: OrdemServicoAtividadeMaterialModel) {
|
||||||
|
this.materiaisAtividade.push(this.formBuilder.group({
|
||||||
|
material_id: [materialExistente ? materialExistente.material_id || null : null],
|
||||||
|
descricao_snapshot: [materialExistente ? materialExistente.descricao_snapshot || '' : '', Validators.required],
|
||||||
|
unidade_snapshot: [materialExistente ? materialExistente.unidade_snapshot || 'UN' : 'UN'],
|
||||||
|
quantidade: [materialExistente ? this.numero(materialExistente.quantidade) : 1, [Validators.required, Validators.min(0.001)]],
|
||||||
|
valor_custo_unitario: [materialExistente ? this.numero(materialExistente.valor_custo_unitario) : 0, [Validators.min(0)]],
|
||||||
|
valor_cobrado_unitario: [materialExistente ? this.numero(materialExistente.valor_cobrado_unitario) : 0, [Validators.min(0)]],
|
||||||
|
cobravel: [materialExistente ? !!materialExistente.cobravel : true],
|
||||||
|
observacoes: [materialExistente ? materialExistente.observacoes || '' : '']
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
removerMaterialAtividade(index: number) {
|
||||||
|
this.materiaisAtividade.removeAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
materialCatalogoSelecionado(index: number, materialId: number) {
|
||||||
|
var material = this.buscarMaterial(materialId);
|
||||||
|
var grupo = this.materiaisAtividade.at(index) as FormGroup;
|
||||||
|
if (!material || !grupo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
grupo.patchValue({
|
||||||
|
descricao_snapshot: material.descricao,
|
||||||
|
unidade_snapshot: material.unidade || 'UN',
|
||||||
|
valor_custo_unitario: this.numero(material.custo_padrao),
|
||||||
|
valor_cobrado_unitario: this.numero(material.preco_venda_padrao)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
registrarPagamento() {
|
||||||
|
this.limparMensagens();
|
||||||
|
if (this.formPagamento.invalid) {
|
||||||
|
this.marcarFormularioComoTocado(this.formPagamento);
|
||||||
|
this.erro = 'Informe a data, o valor e a forma de pagamento.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.salvandoPagamento = true;
|
||||||
|
var valor = this.formPagamento.getRawValue();
|
||||||
|
var payload: OrdemServicoPagamentoModel = {
|
||||||
|
data_pagamento: this.normalizarDataHoraApi(valor.data_pagamento),
|
||||||
|
valor: this.numero(valor.valor),
|
||||||
|
forma_pagamento: valor.forma_pagamento,
|
||||||
|
referencia: this.textoOuNull(valor.referencia),
|
||||||
|
observacoes: this.textoOuNull(valor.observacoes)
|
||||||
|
};
|
||||||
|
|
||||||
|
this.orionService.insertPagamentoOrdemServico(this.ordemServicoId, payload)
|
||||||
|
.then(() => {
|
||||||
|
this.sucesso = 'Pagamento registrado com sucesso.';
|
||||||
|
this.exibirFormPagamento = false;
|
||||||
|
this.resetarFormPagamento();
|
||||||
|
return this.recarregarOrdem();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.erro = this.obterMensagemErro(error, 'Não foi possível registrar o pagamento.');
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.salvandoPagamento = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelarPagamento(pagamento: OrdemServicoPagamentoModel) {
|
||||||
|
if (!pagamento.id || !window.confirm('Deseja realmente cancelar este pagamento?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.orionService.cancelPagamentoOrdemServico(pagamento.id)
|
||||||
|
.then(() => this.recarregarOrdem())
|
||||||
|
.catch(error => {
|
||||||
|
this.erro = this.obterMensagemErro(error, 'Não foi possível cancelar o pagamento.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
registrarOdometro() {
|
||||||
|
this.limparMensagens();
|
||||||
|
if (this.formOdometro.invalid) {
|
||||||
|
this.marcarFormularioComoTocado(this.formOdometro);
|
||||||
|
this.erro = 'Confira os campos da leitura de odômetro.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.salvandoOdometro = true;
|
||||||
|
var valor = this.formOdometro.getRawValue();
|
||||||
|
var payload: OrdemServicoOdometroModel = {
|
||||||
|
tipo_leitura: valor.tipo_leitura,
|
||||||
|
leitura_em: this.normalizarDataHoraApi(valor.leitura_em),
|
||||||
|
odometro_total_segundos: this.numero(valor.odometro_total_segundos),
|
||||||
|
odometro_conectado_segundos: this.numero(valor.odometro_conectado_segundos),
|
||||||
|
odometro_movimento_segundos: this.numero(valor.odometro_movimento_segundos),
|
||||||
|
odometro_parcial_segundos: this.numero(valor.odometro_parcial_segundos),
|
||||||
|
observacoes: this.textoOuNull(valor.observacoes)
|
||||||
|
};
|
||||||
|
|
||||||
|
this.orionService.insertOdometroOrdemServico(this.ordemServicoId, payload)
|
||||||
|
.then(() => {
|
||||||
|
this.sucesso = 'Leitura registrada com sucesso.';
|
||||||
|
this.exibirFormOdometro = false;
|
||||||
|
return this.recarregarOrdem();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.erro = this.obterMensagemErro(error, 'Não foi possível registrar a leitura.');
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.salvandoOdometro = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get atividadesOrdenadas(): OrdemServicoAtividadeModel[] {
|
||||||
|
if (!this.ordem || !this.ordem.atividades) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.ordem.atividades.slice().sort((a, b) => {
|
||||||
|
var dataA = new Date(a.data_atividade).getTime();
|
||||||
|
var dataB = new Date(b.data_atividade).getTime();
|
||||||
|
return dataA === dataB
|
||||||
|
? this.numero(a.ordem_exibicao) - this.numero(b.ordem_exibicao)
|
||||||
|
: dataB - dataA;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
totalHorasAtividade(atividade: OrdemServicoAtividadeModel): number {
|
||||||
|
return this.numero(atividade.horas_expediente) + this.numero(atividade.horas_extras);
|
||||||
|
}
|
||||||
|
|
||||||
|
valorMateriaisAtividade(atividade: OrdemServicoAtividadeModel): number {
|
||||||
|
return (atividade.materiais || []).reduce((total, material) => {
|
||||||
|
return total + this.numero(material.valor_cobrado_total);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
nomeTecnicosAtividade(atividade: OrdemServicoAtividadeModel): string {
|
||||||
|
var nomes = (atividade.tecnicos || []).map(tecnico => {
|
||||||
|
return tecnico.tecnico_nome || ('Técnico #' + tecnico.tecnico_id);
|
||||||
|
});
|
||||||
|
return nomes.length > 0 ? nomes.join(', ') : 'Não informado';
|
||||||
|
}
|
||||||
|
|
||||||
|
get valorPendente(): number {
|
||||||
|
if (!this.ordem) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return Math.max(0, this.numero(this.ordem.valor_total) - this.numero(this.ordem.valor_pago));
|
||||||
|
}
|
||||||
|
|
||||||
|
labelStatus(status: OrdemServicoStatus): string {
|
||||||
|
return this.buscarTexto(this.statusOpcoes, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
labelTipo(tipo: OrdemServicoTipo): string {
|
||||||
|
return this.buscarTexto(this.tipoOpcoes, tipo);
|
||||||
|
}
|
||||||
|
|
||||||
|
labelPagamento(status: OrdemServicoStatusPagamento): string {
|
||||||
|
return this.buscarTexto([
|
||||||
|
{ valor: 'NAO_APLICAVEL', texto: 'Não aplicável' },
|
||||||
|
{ valor: 'PENDENTE', texto: 'Pendente' },
|
||||||
|
{ valor: 'PARCIAL', texto: 'Parcial' },
|
||||||
|
{ valor: 'RECEBIDO', texto: 'Recebido' },
|
||||||
|
{ valor: 'CORTESIA', texto: 'Cortesia' }
|
||||||
|
], status);
|
||||||
|
}
|
||||||
|
|
||||||
|
classeStatus(status: OrdemServicoStatus): string {
|
||||||
|
if (status === 'RETIRADA' || status === 'FINALIZADA') return 'badge-sucesso';
|
||||||
|
if (status === 'AGUARDANDO_RETIRADA' || status === 'AGUARDANDO_PECA' || status === 'AGUARDANDO_APROVACAO') return 'badge-alerta';
|
||||||
|
if (status === 'CANCELADA') return 'badge-erro';
|
||||||
|
if (status === 'RASCUNHO') return 'badge-neutro';
|
||||||
|
return 'badge-info';
|
||||||
|
}
|
||||||
|
|
||||||
|
classePagamento(status: OrdemServicoStatusPagamento): string {
|
||||||
|
if (status === 'RECEBIDO') return 'badge-sucesso';
|
||||||
|
if (status === 'PENDENTE') return 'badge-erro';
|
||||||
|
if (status === 'PARCIAL') return 'badge-alerta';
|
||||||
|
return 'badge-neutro';
|
||||||
|
}
|
||||||
|
|
||||||
|
segundosParaTempo(segundos: number): string {
|
||||||
|
var total = Math.max(0, Math.floor(this.numero(segundos)));
|
||||||
|
var horas = Math.floor(total / 3600);
|
||||||
|
var minutos = Math.floor((total % 3600) / 60);
|
||||||
|
var segundosRestantes = total % 60;
|
||||||
|
return this.preencherZero(horas) + ':' + this.preencherZero(minutos) + ':' + this.preencherZero(segundosRestantes);
|
||||||
|
}
|
||||||
|
|
||||||
|
voltar() {
|
||||||
|
this.router.navigate(['/orion-ordens-servico']);
|
||||||
|
}
|
||||||
|
|
||||||
|
private recarregarOrdem(): Promise<any> {
|
||||||
|
return this.modoEdicao && this.ordemServicoId ? this.carregarOrdem() : Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
private preencherNovoOdometroComUltimaLeitura() {
|
||||||
|
if (!this.ordem || !this.ordem.odometros_da_os || this.ordem.odometros_da_os.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var leituras = this.ordem.odometros_da_os.slice().sort((a, b) => {
|
||||||
|
return new Date(b.leitura_em).getTime() - new Date(a.leitura_em).getTime();
|
||||||
|
});
|
||||||
|
var ultima = leituras[0];
|
||||||
|
|
||||||
|
this.formOdometro.patchValue({
|
||||||
|
odometro_total_segundos: this.numero(ultima.odometro_total_segundos),
|
||||||
|
odometro_conectado_segundos: this.numero(ultima.odometro_conectado_segundos),
|
||||||
|
odometro_movimento_segundos: this.numero(ultima.odometro_movimento_segundos),
|
||||||
|
odometro_parcial_segundos: this.numero(ultima.odometro_parcial_segundos),
|
||||||
|
leitura_em: this.formatarDataHoraLocal(new Date())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private resetarFormAtividade() {
|
||||||
|
this.formAtividade.reset({
|
||||||
|
data_atividade: this.formatarData(new Date()),
|
||||||
|
descricao: '',
|
||||||
|
ordem_exibicao: 0,
|
||||||
|
horas_expediente: 0,
|
||||||
|
horas_extras: 0,
|
||||||
|
valor_hora_expediente: 60,
|
||||||
|
valor_hora_extra: 90,
|
||||||
|
cobravel: true,
|
||||||
|
observacoes_internas: '',
|
||||||
|
observacoes_cliente: '',
|
||||||
|
tecnico_ids: []
|
||||||
|
});
|
||||||
|
this.limparMateriaisAtividade();
|
||||||
|
}
|
||||||
|
|
||||||
|
private resetarFormPagamento() {
|
||||||
|
this.formPagamento.reset({
|
||||||
|
data_pagamento: this.formatarDataHoraLocal(new Date()),
|
||||||
|
valor: 0,
|
||||||
|
forma_pagamento: 'PIX',
|
||||||
|
referencia: '',
|
||||||
|
observacoes: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private limparMateriaisAtividade() {
|
||||||
|
while (this.materiaisAtividade.length > 0) {
|
||||||
|
this.materiaisAtividade.removeAt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private buscarEquipamento(equipamentoId: number): EquipamentoManutencaoModel {
|
||||||
|
var id = this.numero(equipamentoId);
|
||||||
|
for (var i = 0; i < this.equipamentos.length; i++) {
|
||||||
|
if (this.numero(this.equipamentos[i].id) === id) return this.equipamentos[i];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private buscarMaterial(materialId: number): MaterialManutencaoModel {
|
||||||
|
var id = this.numero(materialId);
|
||||||
|
for (var i = 0; i < this.materiais.length; i++) {
|
||||||
|
if (this.numero(this.materiais[i].id) === id) return this.materiais[i];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private buscarTexto(opcoes: any[], valor: string): string {
|
||||||
|
for (var i = 0; i < opcoes.length; i++) {
|
||||||
|
if (opcoes[i].valor === valor) return opcoes[i].texto;
|
||||||
|
}
|
||||||
|
return valor || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private marcarFormularioComoTocado(controle: AbstractControl) {
|
||||||
|
if (controle instanceof FormGroup) {
|
||||||
|
Object.keys(controle.controls).forEach(chave => this.marcarFormularioComoTocado(controle.controls[chave]));
|
||||||
|
} else if (controle instanceof FormArray) {
|
||||||
|
controle.controls.forEach(item => this.marcarFormularioComoTocado(item));
|
||||||
|
} else {
|
||||||
|
controle.markAsTouched();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private formatarData(data: Date): string {
|
||||||
|
return data.getFullYear() + '-' + this.preencherZero(data.getMonth() + 1) + '-' + this.preencherZero(data.getDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
private formatarDataHoraLocal(data: Date): string {
|
||||||
|
return this.formatarData(data) + 'T' + this.preencherZero(data.getHours()) + ':' + this.preencherZero(data.getMinutes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private converterDataApiParaLocal(valor: string): string {
|
||||||
|
if (!valor) return '';
|
||||||
|
var texto = String(valor).replace(' ', 'T');
|
||||||
|
return texto.length >= 16 ? texto.substring(0, 16) : texto;
|
||||||
|
}
|
||||||
|
|
||||||
|
private normalizarDataHoraApi(valor: string): string {
|
||||||
|
if (!valor) return null;
|
||||||
|
var texto = String(valor).replace('T', ' ');
|
||||||
|
return texto.length === 16 ? texto + ':00' : texto;
|
||||||
|
}
|
||||||
|
|
||||||
|
private textoOuNull(valor: any): string {
|
||||||
|
if (valor === null || valor === undefined) return null;
|
||||||
|
var texto = String(valor).trim();
|
||||||
|
return texto ? texto : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private numero(valor: any): number {
|
||||||
|
var convertido = Number(valor);
|
||||||
|
return isNaN(convertido) ? 0 : convertido;
|
||||||
|
}
|
||||||
|
|
||||||
|
private numeroOuNull(valor: any): number {
|
||||||
|
if (valor === null || valor === undefined || valor === '') return null;
|
||||||
|
var convertido = Number(valor);
|
||||||
|
return isNaN(convertido) ? null : convertido;
|
||||||
|
}
|
||||||
|
|
||||||
|
private preencherZero(valor: number): string {
|
||||||
|
return valor < 10 ? '0' + valor : String(valor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private limparMensagens() {
|
||||||
|
this.erro = '';
|
||||||
|
this.sucesso = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private obterMensagemErro(error: any, fallback: string): string {
|
||||||
|
return error && error.message ? error.message : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
baixarPdfCliente() {
|
||||||
|
if (!this.modoEdicao || !this.ordemServicoId || this.baixandoPdf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.baixandoPdf = true;
|
||||||
|
this.erro = '';
|
||||||
|
|
||||||
|
this.orionService
|
||||||
|
.downloadOrdemServicoPdf(this.ordemServicoId, false)
|
||||||
|
.then(blob => {
|
||||||
|
var url = window.URL.createObjectURL(blob);
|
||||||
|
var link = document.createElement('a');
|
||||||
|
|
||||||
|
link.href = url;
|
||||||
|
link.download = 'OS_' + this.ordemServicoId + '.pdf';
|
||||||
|
link.style.display = 'none';
|
||||||
|
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
|
||||||
|
window.setTimeout(() => {
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
}, 1000);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('[OS FORM] Erro ao gerar PDF:', error);
|
||||||
|
this.erro =
|
||||||
|
'Não foi possível gerar o PDF da ordem de serviço.';
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.baixandoPdf = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,387 @@
|
||||||
|
<div class="os-page">
|
||||||
|
|
||||||
|
<div class="page-header">
|
||||||
|
<div>
|
||||||
|
<span class="eyebrow">ORIONTARD</span>
|
||||||
|
<h1>Ordens de Serviço</h1>
|
||||||
|
<p>Histórico de manutenções, horas trabalhadas e valores dos equipamentos.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="header-actions">
|
||||||
|
<button
|
||||||
|
mat-stroked-button
|
||||||
|
type="button"
|
||||||
|
(click)="atualizar()"
|
||||||
|
[disabled]="carregando">
|
||||||
|
Atualizar
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
mat-raised-button
|
||||||
|
color="primary"
|
||||||
|
type="button"
|
||||||
|
(click)="novaOrdem()">
|
||||||
|
Nova OS
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
mat-stroked-button
|
||||||
|
type="button"
|
||||||
|
(click)="baixarRelatorioPdf()"
|
||||||
|
[disabled]="carregando || baixandoRelatorio">
|
||||||
|
{{ baixandoRelatorio ? 'Gerando relatório...' : 'Relatório PDF' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="filters-card">
|
||||||
|
<div class="filters-grid">
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="field-search">
|
||||||
|
<mat-label>Pesquisar</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[(ngModel)]="busca"
|
||||||
|
(keyup.enter)="aplicarFiltros()"
|
||||||
|
placeholder="OS, equipamento, NS ou problema">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Status</mat-label>
|
||||||
|
<mat-select [(ngModel)]="statusSelecionado">
|
||||||
|
<mat-option
|
||||||
|
*ngFor="let opcao of statusOpcoes"
|
||||||
|
[value]="opcao.valor">
|
||||||
|
{{ opcao.texto }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Pagamento</mat-label>
|
||||||
|
<mat-select [(ngModel)]="pagamentoSelecionado">
|
||||||
|
<mat-option
|
||||||
|
*ngFor="let opcao of pagamentoOpcoes"
|
||||||
|
[value]="opcao.valor">
|
||||||
|
{{ opcao.texto }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Tipo</mat-label>
|
||||||
|
<mat-select [(ngModel)]="tipoSelecionado">
|
||||||
|
<mat-option
|
||||||
|
*ngFor="let opcao of tipoOpcoes"
|
||||||
|
[value]="opcao.valor">
|
||||||
|
{{ opcao.texto }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Entrada inicial</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[matDatepicker]="pickerInicio"
|
||||||
|
[(ngModel)]="dataInicio">
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="pickerInicio"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #pickerInicio></mat-datepicker>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Entrada final</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[matDatepicker]="pickerFim"
|
||||||
|
[(ngModel)]="dataFim">
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="pickerFim"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #pickerFim></mat-datepicker>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="filters-actions">
|
||||||
|
<button
|
||||||
|
mat-button
|
||||||
|
type="button"
|
||||||
|
(click)="limparFiltros()"
|
||||||
|
[disabled]="carregando">
|
||||||
|
Limpar
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
mat-raised-button
|
||||||
|
color="primary"
|
||||||
|
type="button"
|
||||||
|
(click)="aplicarFiltros()"
|
||||||
|
[disabled]="carregando">
|
||||||
|
Filtrar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="summary-grid">
|
||||||
|
<article class="summary-card">
|
||||||
|
<span class="summary-label">OS encontradas</span>
|
||||||
|
<strong>{{ totalRegistros }}</strong>
|
||||||
|
<small>{{ dataSource.data.length }} nesta página</small>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="summary-card">
|
||||||
|
<span class="summary-label">Horas na página</span>
|
||||||
|
<strong>{{ totalHorasPagina | number:'1.2-2' }}</strong>
|
||||||
|
<small>Expediente + horas extras</small>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="summary-card">
|
||||||
|
<span class="summary-label">Valor na página</span>
|
||||||
|
<strong>{{ valorTotalPagina | currency:'BRL':'symbol':'1.2-2' }}</strong>
|
||||||
|
<small>Soma das OS exibidas</small>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="summary-card">
|
||||||
|
<span class="summary-label">Pagamentos abertos</span>
|
||||||
|
<strong>{{ pendentesNaPagina }}</strong>
|
||||||
|
<small>Pendentes ou parciais na página</small>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="table-card">
|
||||||
|
|
||||||
|
<div class="loading-state" *ngIf="carregando">
|
||||||
|
<div class="loading-bar"></div>
|
||||||
|
<span>Carregando ordens de serviço...</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="error-state" *ngIf="!carregando && erro">
|
||||||
|
<strong>Não foi possível carregar as OS.</strong>
|
||||||
|
<p>{{ erro }}</p>
|
||||||
|
<button mat-stroked-button type="button" (click)="atualizar()">
|
||||||
|
Tentar novamente
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="empty-state"
|
||||||
|
*ngIf="!carregando && !erro && dataSource.data.length === 0">
|
||||||
|
<strong>Nenhuma ordem de serviço encontrada.</strong>
|
||||||
|
<p>Altere os filtros ou confira se a importação foi concluída.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="table-scroll"
|
||||||
|
*ngIf="!carregando && !erro && dataSource.data.length > 0">
|
||||||
|
|
||||||
|
<table
|
||||||
|
mat-table
|
||||||
|
[dataSource]="dataSource"
|
||||||
|
class="os-table">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="id">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>OS</th>
|
||||||
|
<td mat-cell *matCellDef="let ordem">
|
||||||
|
<span class="os-number">#{{ ordem.id }}</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="equipamento">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Equipamento</th>
|
||||||
|
<td mat-cell *matCellDef="let ordem">
|
||||||
|
<div class="equipment-cell">
|
||||||
|
<strong>{{ nomeEquipamento(ordem) }}</strong>
|
||||||
|
<span *ngIf="ordem.numero_serie_snapshot">
|
||||||
|
NS {{ ordem.numero_serie_snapshot }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="data_entrada">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Entrada</th>
|
||||||
|
<td mat-cell *matCellDef="let ordem">
|
||||||
|
<div class="date-cell">
|
||||||
|
<strong>{{ ordem.data_entrada | date:'dd/MM/yyyy' }}</strong>
|
||||||
|
<span *ngIf="ordem.data_finalizacao">
|
||||||
|
Finalizada em {{ ordem.data_finalizacao | date:'dd/MM/yyyy' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="problema_relatado">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Problema relatado</th>
|
||||||
|
<td mat-cell *matCellDef="let ordem">
|
||||||
|
<span
|
||||||
|
class="problem-text"
|
||||||
|
[title]="ordem.problema_relatado || ''">
|
||||||
|
{{ ordem.problema_relatado || 'Sem descrição' }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="tipo">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Tipo</th>
|
||||||
|
<td mat-cell *matCellDef="let ordem">
|
||||||
|
{{ labelTipo(ordem.tipo) }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="status">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Status</th>
|
||||||
|
<td mat-cell *matCellDef="let ordem">
|
||||||
|
<span
|
||||||
|
class="status-badge"
|
||||||
|
[ngClass]="classeStatus(ordem.status)">
|
||||||
|
{{ labelStatus(ordem.status) }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="status_pagamento">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Pagamento</th>
|
||||||
|
<td mat-cell *matCellDef="let ordem">
|
||||||
|
<span
|
||||||
|
class="status-badge"
|
||||||
|
[ngClass]="classePagamento(ordem.status_pagamento)">
|
||||||
|
{{ labelPagamento(ordem.status_pagamento) }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="horas">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Horas</th>
|
||||||
|
<td mat-cell *matCellDef="let ordem">
|
||||||
|
<div class="hours-cell">
|
||||||
|
<strong>{{ totalHoras(ordem) | number:'1.2-2' }}</strong>
|
||||||
|
<span>
|
||||||
|
{{ ordem.total_horas_expediente | number:'1.2-2' }} exp.
|
||||||
|
·
|
||||||
|
{{ ordem.total_horas_extras | number:'1.2-2' }} extra
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="valor_total">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Total</th>
|
||||||
|
<td mat-cell *matCellDef="let ordem">
|
||||||
|
<div class="money-cell">
|
||||||
|
<strong>
|
||||||
|
{{ ordem.valor_total | currency:'BRL':'symbol':'1.2-2' }}
|
||||||
|
</strong>
|
||||||
|
<span *ngIf="ordem.valor_pendente > 0">
|
||||||
|
Pendente:
|
||||||
|
{{ ordem.valor_pendente | currency:'BRL':'symbol':'1.2-2' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="acoes">
|
||||||
|
<th mat-header-cell *matHeaderCellDef></th>
|
||||||
|
<td mat-cell *matCellDef="let ordem">
|
||||||
|
<div class="row-actions">
|
||||||
|
<button
|
||||||
|
mat-stroked-button
|
||||||
|
type="button"
|
||||||
|
(click)="abrirOrdem(ordem)">
|
||||||
|
Detalhes
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
mat-button
|
||||||
|
type="button"
|
||||||
|
(click)="baixarPdf(ordem)"
|
||||||
|
[disabled]="baixandoPdfId === ordem.id">
|
||||||
|
{{ baixandoPdfId === ordem.id ? 'Gerando...' : 'PDF' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
|
||||||
|
<tr
|
||||||
|
mat-row
|
||||||
|
*matRowDef="let row; columns: displayedColumns;"
|
||||||
|
[class.selected-row]="ordemSelecionada && ordemSelecionada.id === row.id"
|
||||||
|
(dblclick)="selecionarOrdem(row)">
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-paginator
|
||||||
|
*ngIf="!erro && totalRegistros > 0"
|
||||||
|
[length]="totalRegistros"
|
||||||
|
[pageIndex]="paginaAtual - 1"
|
||||||
|
[pageSize]="tamanhoPagina"
|
||||||
|
[pageSizeOptions]="opcoesTamanhoPagina"
|
||||||
|
[disabled]="carregando"
|
||||||
|
showFirstLastButtons
|
||||||
|
(page)="onPageChange($event)">
|
||||||
|
</mat-paginator>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="quick-view" *ngIf="ordemSelecionada">
|
||||||
|
<div class="quick-view-header">
|
||||||
|
<div>
|
||||||
|
<span class="eyebrow">VISUALIZAÇÃO RÁPIDA</span>
|
||||||
|
<h2>OS #{{ ordemSelecionada.id }}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
mat-button
|
||||||
|
type="button"
|
||||||
|
(click)="ordemSelecionada = null">
|
||||||
|
Fechar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="quick-view-grid">
|
||||||
|
<div>
|
||||||
|
<span>Equipamento</span>
|
||||||
|
<strong>{{ nomeEquipamento(ordemSelecionada) }}</strong>
|
||||||
|
<small *ngIf="ordemSelecionada.numero_serie_snapshot">
|
||||||
|
NS {{ ordemSelecionada.numero_serie_snapshot }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span>Entrada</span>
|
||||||
|
<strong>{{ ordemSelecionada.data_entrada | date:'dd/MM/yyyy' }}</strong>
|
||||||
|
<small>
|
||||||
|
{{ labelTipo(ordemSelecionada.tipo) }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span>Horas</span>
|
||||||
|
<strong>{{ totalHoras(ordemSelecionada) | number:'1.2-2' }}</strong>
|
||||||
|
<small>
|
||||||
|
{{ ordemSelecionada.total_horas_extras | number:'1.2-2' }}
|
||||||
|
hora(s) extra(s)
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span>Valor total</span>
|
||||||
|
<strong>
|
||||||
|
{{ ordemSelecionada.valor_total | currency:'BRL':'symbol':'1.2-2' }}
|
||||||
|
</strong>
|
||||||
|
<small>
|
||||||
|
Pago:
|
||||||
|
{{ ordemSelecionada.valor_pago | currency:'BRL':'symbol':'1.2-2' }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="quick-view-problem">
|
||||||
|
<span>Problema relatado</span>
|
||||||
|
<p>{{ ordemSelecionada.problema_relatado || 'Sem descrição' }}</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,630 @@
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
min-height: 100%;
|
||||||
|
background: #f4f6f8;
|
||||||
|
color: #1f2933;
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-page {
|
||||||
|
padding: 24px;
|
||||||
|
max-width: 1680px;
|
||||||
|
margin: 0 auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 24px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 4px 0 6px;
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
color: #667085;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
display: inline-block;
|
||||||
|
color: #1769aa;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 1.2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions,
|
||||||
|
.filters-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters-card,
|
||||||
|
.table-card,
|
||||||
|
.quick-view {
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e4e7ec;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(16, 24, 40, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters-card {
|
||||||
|
padding: 18px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns:
|
||||||
|
minmax(260px, 2fr)
|
||||||
|
repeat(3, minmax(150px, 1fr))
|
||||||
|
repeat(2, minmax(150px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters-grid .mat-form-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters-actions {
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(180px, 1fr));
|
||||||
|
gap: 14px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card {
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e4e7ec;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px 18px;
|
||||||
|
box-shadow: 0 2px 8px rgba(16, 24, 40, 0.03);
|
||||||
|
|
||||||
|
strong {
|
||||||
|
display: block;
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1.2;
|
||||||
|
color: #101828;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
display: block;
|
||||||
|
margin-top: 5px;
|
||||||
|
color: #98a2b3;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-label {
|
||||||
|
color: #667085;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-card {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-scroll {
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-table {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 1380px;
|
||||||
|
border-collapse: collapse;
|
||||||
|
|
||||||
|
th.mat-header-cell {
|
||||||
|
background: #f8fafc;
|
||||||
|
color: #475467;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.45px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.mat-cell,
|
||||||
|
th.mat-header-cell {
|
||||||
|
padding: 12px 14px;
|
||||||
|
border-bottom-color: #edf0f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.mat-row {
|
||||||
|
transition: background 120ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.mat-row:hover {
|
||||||
|
background: #f8fbff;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.selected-row {
|
||||||
|
background: #eef6ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-number {
|
||||||
|
color: #1769aa;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.equipment-cell,
|
||||||
|
.date-cell,
|
||||||
|
.hours-cell,
|
||||||
|
.money-cell {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3px;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: #344054;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #98a2b3;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.problem-text {
|
||||||
|
display: block;
|
||||||
|
max-width: 340px;
|
||||||
|
color: #475467;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 24px;
|
||||||
|
padding: 2px 9px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-sucesso {
|
||||||
|
background: #ecfdf3;
|
||||||
|
color: #027a48;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-processando {
|
||||||
|
background: #eff8ff;
|
||||||
|
color: #175cd3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-alerta {
|
||||||
|
background: #fffaeb;
|
||||||
|
color: #b54708;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-erro {
|
||||||
|
background: #fef3f2;
|
||||||
|
color: #b42318;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-info {
|
||||||
|
background: #f4f3ff;
|
||||||
|
color: #5925dc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-neutro {
|
||||||
|
background: #f2f4f7;
|
||||||
|
color: #475467;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-state,
|
||||||
|
.error-state,
|
||||||
|
.empty-state {
|
||||||
|
min-height: 220px;
|
||||||
|
padding: 36px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: #344054;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
span {
|
||||||
|
color: #667085;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-bar {
|
||||||
|
width: 160px;
|
||||||
|
height: 4px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border-radius: 999px;
|
||||||
|
overflow: hidden;
|
||||||
|
background:
|
||||||
|
linear-gradient(
|
||||||
|
90deg,
|
||||||
|
#d0d5dd 0%,
|
||||||
|
#1769aa 45%,
|
||||||
|
#d0d5dd 100%
|
||||||
|
);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: loading 1.2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading {
|
||||||
|
from {
|
||||||
|
background-position: 200% 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
background-position: -200% 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-view {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-view-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 4px 0 0;
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-view-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(160px, 1fr));
|
||||||
|
gap: 14px;
|
||||||
|
margin-top: 18px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
padding: 14px;
|
||||||
|
background: #f8fafc;
|
||||||
|
border-radius: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span,
|
||||||
|
small {
|
||||||
|
display: block;
|
||||||
|
color: #667085;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
display: block;
|
||||||
|
margin: 5px 0;
|
||||||
|
color: #344054;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-view-problem {
|
||||||
|
margin-top: 14px;
|
||||||
|
padding: 14px;
|
||||||
|
border: 1px solid #eaecf0;
|
||||||
|
border-radius: 9px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #667085;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 7px 0 0;
|
||||||
|
color: #344054;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-paginator {
|
||||||
|
border-top: 1px solid #edf0f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1250px) {
|
||||||
|
.filters-grid {
|
||||||
|
grid-template-columns: repeat(3, minmax(180px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-grid,
|
||||||
|
.quick-view-grid {
|
||||||
|
grid-template-columns: repeat(2, minmax(180px, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 760px) {
|
||||||
|
.os-page {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
button {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters-grid,
|
||||||
|
.summary-grid,
|
||||||
|
.quick-view-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters-actions {
|
||||||
|
button {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// ISOLAMENTO CONTRA ESTILOS GLOBAIS E TEMA ESCURO DO MATERIAL
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f4f6f8;
|
||||||
|
color: #1f2933;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------
|
||||||
|
CAMPOS DOS FILTROS
|
||||||
|
------------------------------------------------------------ */
|
||||||
|
|
||||||
|
:host ::ng-deep .filters-card .mat-form-field {
|
||||||
|
color: #101828 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .filters-card .mat-input-element {
|
||||||
|
color: #101828 !important;
|
||||||
|
caret-color: #1769aa !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .filters-card .mat-select-value,
|
||||||
|
:host ::ng-deep .filters-card .mat-select-value-text {
|
||||||
|
color: #101828 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .filters-card .mat-form-field-label {
|
||||||
|
color: #667085 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .filters-card .mat-form-field.mat-focused .mat-form-field-label {
|
||||||
|
color: #1769aa !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .filters-card .mat-form-field-outline {
|
||||||
|
color: #d0d5dd !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .filters-card .mat-form-field-outline-thick {
|
||||||
|
color: #1769aa !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .filters-card .mat-select-arrow {
|
||||||
|
color: #667085 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .filters-card .mat-datepicker-toggle {
|
||||||
|
color: #667085 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .filters-card input::placeholder {
|
||||||
|
color: #98a2b3 !important;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove qualquer fundo escuro herdado */
|
||||||
|
:host ::ng-deep .filters-card .mat-form-field-flex {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------
|
||||||
|
TABELA
|
||||||
|
------------------------------------------------------------ */
|
||||||
|
|
||||||
|
:host ::ng-deep .os-table,
|
||||||
|
:host ::ng-deep table.os-table {
|
||||||
|
background: #ffffff !important;
|
||||||
|
color: #344054 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-table tr.mat-header-row {
|
||||||
|
background: #f8fafc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-table tr.mat-row {
|
||||||
|
background: #ffffff !important;
|
||||||
|
color: #344054 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sobrescreve o tr:nth-child(even) global.
|
||||||
|
*/
|
||||||
|
:host ::ng-deep .os-table tr.mat-row:nth-child(even) {
|
||||||
|
background: #fbfcfd !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-table tr.mat-row:hover {
|
||||||
|
background: #f2f7fc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-table tr.mat-row.selected-row {
|
||||||
|
background: #eaf4ff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-table th.mat-header-cell {
|
||||||
|
background: #f8fafc !important;
|
||||||
|
color: #475467 !important;
|
||||||
|
border-color: #eaecf0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-table td.mat-cell {
|
||||||
|
background: transparent !important;
|
||||||
|
color: #344054 !important;
|
||||||
|
border-color: #eaecf0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-table td,
|
||||||
|
:host ::ng-deep .os-table th {
|
||||||
|
color: #344054 !important;
|
||||||
|
border-left: 0 !important;
|
||||||
|
border-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Textos internos das células */
|
||||||
|
:host ::ng-deep .os-table .equipment-cell strong,
|
||||||
|
:host ::ng-deep .os-table .date-cell strong,
|
||||||
|
:host ::ng-deep .os-table .hours-cell strong,
|
||||||
|
:host ::ng-deep .os-table .money-cell strong {
|
||||||
|
color: #344054 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-table .equipment-cell span,
|
||||||
|
:host ::ng-deep .os-table .date-cell span,
|
||||||
|
:host ::ng-deep .os-table .hours-cell span,
|
||||||
|
:host ::ng-deep .os-table .money-cell span {
|
||||||
|
color: #667085 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-table .problem-text {
|
||||||
|
color: #475467 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .os-table .os-number {
|
||||||
|
color: #1769aa !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------
|
||||||
|
BOTÕES
|
||||||
|
------------------------------------------------------------ */
|
||||||
|
|
||||||
|
:host ::ng-deep .header-actions .mat-stroked-button,
|
||||||
|
:host ::ng-deep .filters-actions .mat-button,
|
||||||
|
:host ::ng-deep .os-table .mat-stroked-button,
|
||||||
|
:host ::ng-deep .quick-view .mat-button {
|
||||||
|
color: #344054 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .header-actions .mat-stroked-button,
|
||||||
|
:host ::ng-deep .os-table .mat-stroked-button {
|
||||||
|
border-color: #d0d5dd !important;
|
||||||
|
background: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .header-actions .mat-raised-button[disabled] {
|
||||||
|
background: #e4e7ec !important;
|
||||||
|
color: #98a2b3 !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .filters-actions .mat-raised-button {
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------
|
||||||
|
PAGINADOR
|
||||||
|
------------------------------------------------------------ */
|
||||||
|
|
||||||
|
:host ::ng-deep .mat-paginator {
|
||||||
|
background: #ffffff !important;
|
||||||
|
color: #475467 !important;
|
||||||
|
border-top: 1px solid #eaecf0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .mat-paginator .mat-select-value,
|
||||||
|
:host ::ng-deep .mat-paginator .mat-select-value-text {
|
||||||
|
color: #344054 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .mat-paginator .mat-select-arrow {
|
||||||
|
color: #667085 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .mat-paginator .mat-icon-button {
|
||||||
|
color: #475467 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .mat-paginator .mat-icon-button[disabled] {
|
||||||
|
color: #d0d5dd !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------
|
||||||
|
BADGES
|
||||||
|
------------------------------------------------------------ */
|
||||||
|
|
||||||
|
:host ::ng-deep .status-badge.badge-sucesso {
|
||||||
|
background: #ecfdf3 !important;
|
||||||
|
color: #027a48 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .status-badge.badge-processando {
|
||||||
|
background: #eff8ff !important;
|
||||||
|
color: #175cd3 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .status-badge.badge-alerta {
|
||||||
|
background: #fffaeb !important;
|
||||||
|
color: #b54708 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .status-badge.badge-erro {
|
||||||
|
background: #fef3f2 !important;
|
||||||
|
color: #b42318 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .status-badge.badge-info {
|
||||||
|
background: #f4f3ff !important;
|
||||||
|
color: #5925dc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .status-badge.badge-neutro {
|
||||||
|
background: #f2f4f7 !important;
|
||||||
|
color: #475467 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { OrionOrdensServicoComponent } from './orion-ordens-servico.component';
|
||||||
|
|
||||||
|
describe('OrionOrdensServicoComponent', () => {
|
||||||
|
let component: OrionOrdensServicoComponent;
|
||||||
|
let fixture: ComponentFixture<OrionOrdensServicoComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ OrionOrdensServicoComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(OrionOrdensServicoComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,429 @@
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { PageEvent } from '@angular/material/paginator';
|
||||||
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
import {
|
||||||
|
OrdemServicoFiltrosModel,
|
||||||
|
OrdemServicoListaModel,
|
||||||
|
OrdemServicoStatus,
|
||||||
|
OrdemServicoStatusPagamento,
|
||||||
|
OrdemServicoTipo
|
||||||
|
} from '../../models/orionModel';
|
||||||
|
|
||||||
|
import { OrionService } from '../../services/orionService';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-orion-ordens-servico',
|
||||||
|
templateUrl: './orion-ordens-servico.component.html',
|
||||||
|
styleUrls: ['./orion-ordens-servico.component.scss']
|
||||||
|
})
|
||||||
|
export class OrionOrdensServicoComponent implements OnInit {
|
||||||
|
|
||||||
|
displayedColumns: string[] = [
|
||||||
|
'id',
|
||||||
|
'equipamento',
|
||||||
|
'data_entrada',
|
||||||
|
'problema_relatado',
|
||||||
|
'tipo',
|
||||||
|
'status',
|
||||||
|
'status_pagamento',
|
||||||
|
'horas',
|
||||||
|
'valor_total',
|
||||||
|
'acoes'
|
||||||
|
];
|
||||||
|
|
||||||
|
dataSource = new MatTableDataSource<OrdemServicoListaModel>([]);
|
||||||
|
ordemSelecionada: OrdemServicoListaModel = null;
|
||||||
|
|
||||||
|
carregando: boolean = false;
|
||||||
|
erro: string = '';
|
||||||
|
|
||||||
|
paginaAtual: number = 1;
|
||||||
|
tamanhoPagina: number = 25;
|
||||||
|
totalRegistros: number = 0;
|
||||||
|
totalPaginas: number = 0;
|
||||||
|
opcoesTamanhoPagina: number[] = [10, 25, 50, 100];
|
||||||
|
|
||||||
|
busca: string = '';
|
||||||
|
statusSelecionado: OrdemServicoStatus | '' = '';
|
||||||
|
pagamentoSelecionado: OrdemServicoStatusPagamento | '' = '';
|
||||||
|
tipoSelecionado: OrdemServicoTipo | '' = '';
|
||||||
|
dataInicio: Date = null;
|
||||||
|
dataFim: Date = null;
|
||||||
|
|
||||||
|
statusOpcoes = [
|
||||||
|
{ valor: '', texto: 'Todos os status' },
|
||||||
|
{ valor: 'RASCUNHO', texto: 'Rascunho' },
|
||||||
|
{ valor: 'ABERTA', texto: 'Aberta' },
|
||||||
|
{ valor: 'EM_DIAGNOSTICO', texto: 'Em diagnóstico' },
|
||||||
|
{ valor: 'AGUARDANDO_APROVACAO', texto: 'Aguardando aprovação' },
|
||||||
|
{ valor: 'AGUARDANDO_PECA', texto: 'Aguardando peça' },
|
||||||
|
{ valor: 'EM_EXECUCAO', texto: 'Em execução' },
|
||||||
|
{ valor: 'EM_TESTES', texto: 'Em testes' },
|
||||||
|
{ valor: 'FINALIZADA', texto: 'Finalizada' },
|
||||||
|
{ valor: 'RETIRADA', texto: 'Retirada' },
|
||||||
|
{ valor: 'CANCELADA', texto: 'Cancelada' }
|
||||||
|
];
|
||||||
|
|
||||||
|
pagamentoOpcoes = [
|
||||||
|
{ valor: '', texto: 'Todos os pagamentos' },
|
||||||
|
{ valor: 'NAO_APLICAVEL', texto: 'Não aplicável' },
|
||||||
|
{ valor: 'PENDENTE', texto: 'Pendente' },
|
||||||
|
{ valor: 'PARCIAL', texto: 'Parcial' },
|
||||||
|
{ valor: 'RECEBIDO', texto: 'Recebido' },
|
||||||
|
{ valor: 'CORTESIA', texto: 'Cortesia' }
|
||||||
|
];
|
||||||
|
|
||||||
|
tipoOpcoes = [
|
||||||
|
{ valor: '', texto: 'Todos os tipos' },
|
||||||
|
{ valor: 'CORRETIVA', texto: 'Corretiva' },
|
||||||
|
{ valor: 'PREVENTIVA', texto: 'Preventiva' },
|
||||||
|
{ valor: 'REFORMA', texto: 'Reforma' },
|
||||||
|
{ valor: 'DESENVOLVIMENTO', texto: 'Desenvolvimento' },
|
||||||
|
{ valor: 'FABRICACAO', texto: 'Fabricação' },
|
||||||
|
{ valor: 'GARANTIA', texto: 'Garantia' },
|
||||||
|
{ valor: 'CORTESIA', texto: 'Cortesia' },
|
||||||
|
{ valor: 'INTERNA', texto: 'Interna' },
|
||||||
|
{ valor: 'OUTRO', texto: 'Outro' }
|
||||||
|
];
|
||||||
|
|
||||||
|
baixandoPdfId: number = null;
|
||||||
|
baixandoRelatorio: boolean = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private orionService: OrionService,
|
||||||
|
private router: Router
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.carregarOrdens();
|
||||||
|
}
|
||||||
|
|
||||||
|
carregarOrdens() {
|
||||||
|
this.carregando = true;
|
||||||
|
this.erro = '';
|
||||||
|
|
||||||
|
var filtros: OrdemServicoFiltrosModel = {
|
||||||
|
page: this.paginaAtual,
|
||||||
|
pageSize: this.tamanhoPagina,
|
||||||
|
busca: this.normalizarTexto(this.busca),
|
||||||
|
status: this.statusSelecionado,
|
||||||
|
status_pagamento: this.pagamentoSelecionado,
|
||||||
|
tipo: this.tipoSelecionado,
|
||||||
|
data_inicio: this.formatarDataApi(this.dataInicio),
|
||||||
|
data_fim: this.formatarDataApi(this.dataFim)
|
||||||
|
};
|
||||||
|
|
||||||
|
this.orionService
|
||||||
|
.getOrdensServico(filtros)
|
||||||
|
.then(resultado => {
|
||||||
|
this.dataSource.data = resultado.items || [];
|
||||||
|
|
||||||
|
this.paginaAtual = resultado.pagination.page || 1;
|
||||||
|
this.tamanhoPagina = resultado.pagination.pageSize || 25;
|
||||||
|
this.totalRegistros = resultado.pagination.total || 0;
|
||||||
|
this.totalPaginas = resultado.pagination.totalPages || 0;
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.ordemSelecionada &&
|
||||||
|
!this.dataSource.data.some(
|
||||||
|
ordem => ordem.id === this.ordemSelecionada.id
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
this.ordemSelecionada = null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('[OS] Erro ao carregar ordens:', error);
|
||||||
|
|
||||||
|
this.dataSource.data = [];
|
||||||
|
this.totalRegistros = 0;
|
||||||
|
this.totalPaginas = 0;
|
||||||
|
|
||||||
|
this.erro = error && error.message
|
||||||
|
? error.message
|
||||||
|
: 'Não foi possível carregar as ordens de serviço.';
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.carregando = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
novaOrdem() {
|
||||||
|
this.router.navigate([
|
||||||
|
'/orion-ordens-servico',
|
||||||
|
'nova'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
abrirOrdem(ordem: OrdemServicoListaModel) {
|
||||||
|
this.router.navigate([
|
||||||
|
'/orion-ordens-servico',
|
||||||
|
ordem.id,
|
||||||
|
'editar'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
aplicarFiltros() {
|
||||||
|
this.paginaAtual = 1;
|
||||||
|
this.ordemSelecionada = null;
|
||||||
|
this.carregarOrdens();
|
||||||
|
}
|
||||||
|
|
||||||
|
limparFiltros() {
|
||||||
|
this.busca = '';
|
||||||
|
this.statusSelecionado = '';
|
||||||
|
this.pagamentoSelecionado = '';
|
||||||
|
this.tipoSelecionado = '';
|
||||||
|
this.dataInicio = null;
|
||||||
|
this.dataFim = null;
|
||||||
|
this.paginaAtual = 1;
|
||||||
|
this.ordemSelecionada = null;
|
||||||
|
|
||||||
|
this.carregarOrdens();
|
||||||
|
}
|
||||||
|
|
||||||
|
atualizar() {
|
||||||
|
this.carregarOrdens();
|
||||||
|
}
|
||||||
|
|
||||||
|
onPageChange(event: PageEvent) {
|
||||||
|
this.paginaAtual = event.pageIndex + 1;
|
||||||
|
this.tamanhoPagina = event.pageSize;
|
||||||
|
this.ordemSelecionada = null;
|
||||||
|
|
||||||
|
this.carregarOrdens();
|
||||||
|
}
|
||||||
|
|
||||||
|
selecionarOrdem(ordem: OrdemServicoListaModel) {
|
||||||
|
this.ordemSelecionada = ordem;
|
||||||
|
}
|
||||||
|
|
||||||
|
trackById(index: number, ordem: OrdemServicoListaModel): number {
|
||||||
|
return ordem.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
get totalHorasPagina(): number {
|
||||||
|
return this.dataSource.data.reduce(
|
||||||
|
(total, ordem) =>
|
||||||
|
total +
|
||||||
|
this.numero(ordem.total_horas_expediente) +
|
||||||
|
this.numero(ordem.total_horas_extras),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get valorTotalPagina(): number {
|
||||||
|
return this.dataSource.data.reduce(
|
||||||
|
(total, ordem) => total + this.numero(ordem.valor_total),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get pendentesNaPagina(): number {
|
||||||
|
return this.dataSource.data.filter(
|
||||||
|
ordem =>
|
||||||
|
ordem.status_pagamento === 'PENDENTE' ||
|
||||||
|
ordem.status_pagamento === 'PARCIAL'
|
||||||
|
).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
totalHoras(ordem: OrdemServicoListaModel): number {
|
||||||
|
return (
|
||||||
|
this.numero(ordem.total_horas_expediente) +
|
||||||
|
this.numero(ordem.total_horas_extras)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
nomeEquipamento(ordem: OrdemServicoListaModel): string {
|
||||||
|
return (
|
||||||
|
ordem.equipamento_nome_snapshot ||
|
||||||
|
ordem.modelo_snapshot ||
|
||||||
|
'Equipamento não informado'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
labelStatus(status: OrdemServicoStatus): string {
|
||||||
|
return this.buscarTextoOpcao(this.statusOpcoes, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
labelPagamento(status: OrdemServicoStatusPagamento): string {
|
||||||
|
return this.buscarTextoOpcao(this.pagamentoOpcoes, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
labelTipo(tipo: OrdemServicoTipo): string {
|
||||||
|
return this.buscarTextoOpcao(this.tipoOpcoes, tipo);
|
||||||
|
}
|
||||||
|
|
||||||
|
classeStatus(status: OrdemServicoStatus): string {
|
||||||
|
switch (status) {
|
||||||
|
case 'RETIRADA':
|
||||||
|
case 'FINALIZADA':
|
||||||
|
return 'badge-sucesso';
|
||||||
|
|
||||||
|
case 'EM_EXECUCAO':
|
||||||
|
case 'EM_DIAGNOSTICO':
|
||||||
|
case 'EM_TESTES':
|
||||||
|
return 'badge-processando';
|
||||||
|
|
||||||
|
case 'AGUARDANDO_APROVACAO':
|
||||||
|
case 'AGUARDANDO_PECA':
|
||||||
|
return 'badge-alerta';
|
||||||
|
|
||||||
|
case 'CANCELADA':
|
||||||
|
return 'badge-erro';
|
||||||
|
|
||||||
|
case 'RASCUNHO':
|
||||||
|
return 'badge-neutro';
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 'badge-info';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classePagamento(status: OrdemServicoStatusPagamento): string {
|
||||||
|
switch (status) {
|
||||||
|
case 'RECEBIDO':
|
||||||
|
return 'badge-sucesso';
|
||||||
|
|
||||||
|
case 'PARCIAL':
|
||||||
|
return 'badge-alerta';
|
||||||
|
|
||||||
|
case 'PENDENTE':
|
||||||
|
return 'badge-erro';
|
||||||
|
|
||||||
|
case 'CORTESIA':
|
||||||
|
return 'badge-info';
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 'badge-neutro';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private buscarTextoOpcao(opcoes: any[], valor: string): string {
|
||||||
|
for (var i = 0; i < opcoes.length; i++) {
|
||||||
|
if (opcoes[i].valor === valor) {
|
||||||
|
return opcoes[i].texto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return valor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private normalizarTexto(valor: string): string {
|
||||||
|
return valor ? valor.trim() : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private formatarDataApi(data: Date): string {
|
||||||
|
if (!data) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
var ano = data.getFullYear();
|
||||||
|
var mes = data.getMonth() + 1;
|
||||||
|
var dia = data.getDate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
ano +
|
||||||
|
'-' +
|
||||||
|
(mes < 10 ? '0' : '') + mes +
|
||||||
|
'-' +
|
||||||
|
(dia < 10 ? '0' : '') + dia
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private numero(valor: any): number {
|
||||||
|
var convertido = Number(valor);
|
||||||
|
return isNaN(convertido) ? 0 : convertido;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
baixarPdf(ordem: OrdemServicoListaModel) {
|
||||||
|
if (!ordem || this.baixandoPdfId !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.baixandoPdfId = ordem.id;
|
||||||
|
|
||||||
|
this.orionService
|
||||||
|
.downloadOrdemServicoPdf(ordem.id, false)
|
||||||
|
.then(blob => {
|
||||||
|
this.salvarBlob(
|
||||||
|
blob,
|
||||||
|
'OS_' +
|
||||||
|
ordem.id +
|
||||||
|
'_' +
|
||||||
|
this.nomeEquipamento(ordem).replace(/[^a-zA-Z0-9]+/g, '_') +
|
||||||
|
'.pdf'
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('[OS] Erro ao baixar PDF:', error);
|
||||||
|
this.erro =
|
||||||
|
'Não foi possível gerar o PDF da ordem de serviço.';
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.baixandoPdfId = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
baixarRelatorioPdf() {
|
||||||
|
if (this.baixandoRelatorio) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.baixandoRelatorio = true;
|
||||||
|
this.erro = '';
|
||||||
|
|
||||||
|
var filtros: any = {
|
||||||
|
busca: this.normalizarTexto(this.busca),
|
||||||
|
status: this.statusSelecionado,
|
||||||
|
status_pagamento: this.pagamentoSelecionado,
|
||||||
|
tipo: this.tipoSelecionado,
|
||||||
|
data_inicio: this.formatarDataApi(this.dataInicio),
|
||||||
|
data_fim: this.formatarDataApi(this.dataFim),
|
||||||
|
|
||||||
|
// Para fechamento mensal, a data de finalização é o critério mais seguro.
|
||||||
|
criterio: 'data_finalizacao'
|
||||||
|
};
|
||||||
|
|
||||||
|
this.orionService
|
||||||
|
.downloadRelatorioOrdensServicoPdf(filtros)
|
||||||
|
.then(blob => {
|
||||||
|
var inicio = filtros.data_inicio || 'inicio';
|
||||||
|
var fim = filtros.data_fim || 'fim';
|
||||||
|
|
||||||
|
this.salvarBlob(
|
||||||
|
blob,
|
||||||
|
'Relatorio_OS_' + inicio + '_' + fim + '.pdf'
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('[OS] Erro ao baixar relatório:', error);
|
||||||
|
this.erro =
|
||||||
|
'Não foi possível gerar o relatório de serviços.';
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.baixandoRelatorio = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private salvarBlob(blob: Blob, nomeArquivo: string) {
|
||||||
|
var url = window.URL.createObjectURL(blob);
|
||||||
|
var link = document.createElement('a');
|
||||||
|
|
||||||
|
link.href = url;
|
||||||
|
link.download = nomeArquivo;
|
||||||
|
link.style.display = 'none';
|
||||||
|
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
|
||||||
|
window.setTimeout(() => {
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,31 +1,489 @@
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { environment } from 'src/environments/environment';
|
import { environment } from 'src/environments/environment';
|
||||||
import { GeoCoordenadasResponseModel, OrionLogsModel } from '../models/orionModel';
|
|
||||||
|
import {
|
||||||
|
ClienteManutencaoModel,
|
||||||
|
EquipamentoManutencaoModel,
|
||||||
|
GeoCoordenadasResponseModel,
|
||||||
|
MaterialManutencaoModel,
|
||||||
|
OrionApiResponseModel,
|
||||||
|
OrionLogInsertModel,
|
||||||
|
OrionLogsModel,
|
||||||
|
OrdemServicoAtividadeSalvarModel,
|
||||||
|
OrdemServicoDetalhesModel,
|
||||||
|
OrdemServicoFiltrosModel,
|
||||||
|
OrdemServicoListaModel,
|
||||||
|
OrdemServicoListaResponseModel,
|
||||||
|
OrdemServicoOdometroModel,
|
||||||
|
OrdemServicoOperacaoResultModel,
|
||||||
|
OrdemServicoPagamentoModel,
|
||||||
|
OrdemServicoSalvarModel,
|
||||||
|
TecnicoManutencaoModel
|
||||||
|
} from '../models/orionModel';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class OrionService {
|
export class OrionService {
|
||||||
|
|
||||||
Rota: String = environment.ApiUrl + "/otp";
|
private readonly Rota: string = environment.ApiUrl + '/otp';
|
||||||
|
|
||||||
constructor(private http: HttpClient) { }
|
constructor(private http: HttpClient) { }
|
||||||
|
|
||||||
getAllLogs() {
|
// ==========================================================
|
||||||
return this.http.get<OrionLogsModel[]>(`${this.Rota}/getAllLogs`)
|
// RESPOSTA PADRÃO
|
||||||
.toPromise()
|
// ==========================================================
|
||||||
.then(response => <OrionLogsModel[]>response)
|
|
||||||
.then(data => {
|
private executar<T>(
|
||||||
return <OrionLogsModel[]>data['response'];
|
requisicao: Promise<OrionApiResponseModel<T>>
|
||||||
})
|
): Promise<T> {
|
||||||
|
return requisicao.then(resultado => {
|
||||||
|
if (!resultado) {
|
||||||
|
throw new Error('A API não retornou uma resposta.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultado.status >= 400 || resultado.error) {
|
||||||
|
const mensagem = resultado.error && resultado.error.message
|
||||||
|
? resultado.error.message
|
||||||
|
: 'Não foi possível concluir a operação.';
|
||||||
|
|
||||||
|
throw new Error(mensagem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultado.response;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
coordenadaToEndereco(Coordenadas: string) {
|
private montarParametros(filtros?: any): HttpParams {
|
||||||
return this.http.get<GeoCoordenadasResponseModel>(`http://api.positionstack.com/v1/reverse?access_key=${environment.GeoAPI}&query=${Coordenadas}`)
|
let params = new HttpParams();
|
||||||
.toPromise()
|
|
||||||
.then(response => <GeoCoordenadasResponseModel>response)
|
if (!filtros) {
|
||||||
.then(data => {
|
return params;
|
||||||
return data;
|
}
|
||||||
})
|
|
||||||
|
Object.keys(filtros).forEach(chave => {
|
||||||
|
const valor = filtros[chave];
|
||||||
|
|
||||||
|
if (valor !== undefined && valor !== null && valor !== '') {
|
||||||
|
params = params.set(chave, String(valor));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
private numero(valor: any): number {
|
||||||
|
const convertido = Number(valor);
|
||||||
|
return Number.isFinite(convertido) ? convertido : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private normalizarOrdemLista(
|
||||||
|
ordem: OrdemServicoListaModel
|
||||||
|
): OrdemServicoListaModel {
|
||||||
|
return {
|
||||||
|
...ordem,
|
||||||
|
id: this.numero(ordem.id),
|
||||||
|
cliente_id: ordem.cliente_id ? this.numero(ordem.cliente_id) : null,
|
||||||
|
equipamento_id: ordem.equipamento_id ? this.numero(ordem.equipamento_id) : null,
|
||||||
|
tecnico_responsavel_id: ordem.tecnico_responsavel_id
|
||||||
|
? this.numero(ordem.tecnico_responsavel_id)
|
||||||
|
: null,
|
||||||
|
|
||||||
|
total_horas_expediente: this.numero(ordem.total_horas_expediente),
|
||||||
|
total_horas_extras: this.numero(ordem.total_horas_extras),
|
||||||
|
|
||||||
|
valor_mao_obra: this.numero(ordem.valor_mao_obra),
|
||||||
|
valor_insumos: this.numero(ordem.valor_insumos),
|
||||||
|
valor_desconto: this.numero(ordem.valor_desconto),
|
||||||
|
valor_acrescimo: this.numero(ordem.valor_acrescimo),
|
||||||
|
valor_total: this.numero(ordem.valor_total),
|
||||||
|
valor_pago: this.numero(ordem.valor_pago),
|
||||||
|
valor_pendente: this.numero(ordem.valor_pendente)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// LOGS
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
getAllLogs(): Promise<OrionLogsModel[]> {
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.get<OrionApiResponseModel<OrionLogsModel[]>>(
|
||||||
|
`${this.Rota}/getAllLogs`
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
insertLog(log: OrionLogInsertModel): Promise<{ id: number }> {
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.post<OrionApiResponseModel<{ id: number }>>(
|
||||||
|
`${this.Rota}/insertLog`,
|
||||||
|
log
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
coordenadaToEndereco(
|
||||||
|
coordenadas: string
|
||||||
|
): Promise<GeoCoordenadasResponseModel> {
|
||||||
|
return this.http
|
||||||
|
.get<GeoCoordenadasResponseModel>(
|
||||||
|
`http://api.positionstack.com/v1/reverse` +
|
||||||
|
`?access_key=${environment.GeoAPI}` +
|
||||||
|
`&query=${encodeURIComponent(coordenadas)}`
|
||||||
|
)
|
||||||
|
.toPromise();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// CADASTROS AUXILIARES
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
getClientesManutencao(): Promise<ClienteManutencaoModel[]> {
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.get<OrionApiResponseModel<ClienteManutencaoModel[]>>(
|
||||||
|
`${this.Rota}/getClientesManutencao`
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getEquipamentosManutencao(): Promise<EquipamentoManutencaoModel[]> {
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.get<OrionApiResponseModel<EquipamentoManutencaoModel[]>>(
|
||||||
|
`${this.Rota}/getEquipamentosManutencao`
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
).then(equipamentos =>
|
||||||
|
equipamentos.map(equipamento => ({
|
||||||
|
...equipamento,
|
||||||
|
id: this.numero(equipamento.id),
|
||||||
|
cliente_id: equipamento.cliente_id
|
||||||
|
? this.numero(equipamento.cliente_id)
|
||||||
|
: null,
|
||||||
|
modelo_id: equipamento.modelo_id
|
||||||
|
? this.numero(equipamento.modelo_id)
|
||||||
|
: null,
|
||||||
|
odometro_total_segundos: this.numero(
|
||||||
|
equipamento.odometro_total_segundos
|
||||||
|
),
|
||||||
|
odometro_conectado_segundos: this.numero(
|
||||||
|
equipamento.odometro_conectado_segundos
|
||||||
|
),
|
||||||
|
odometro_movimento_segundos: this.numero(
|
||||||
|
equipamento.odometro_movimento_segundos
|
||||||
|
),
|
||||||
|
odometro_parcial_segundos: this.numero(
|
||||||
|
equipamento.odometro_parcial_segundos
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTecnicosManutencao(): Promise<TecnicoManutencaoModel[]> {
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.get<OrionApiResponseModel<TecnicoManutencaoModel[]>>(
|
||||||
|
`${this.Rota}/getTecnicosManutencao`
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
).then(tecnicos =>
|
||||||
|
tecnicos.map(tecnico => ({
|
||||||
|
...tecnico,
|
||||||
|
id: this.numero(tecnico.id),
|
||||||
|
usuario_sistema_id: tecnico.usuario_sistema_id
|
||||||
|
? this.numero(tecnico.usuario_sistema_id)
|
||||||
|
: null,
|
||||||
|
valor_hora_expediente_padrao: this.numero(
|
||||||
|
tecnico.valor_hora_expediente_padrao
|
||||||
|
),
|
||||||
|
valor_hora_extra_padrao: this.numero(
|
||||||
|
tecnico.valor_hora_extra_padrao
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMateriaisManutencao(): Promise<MaterialManutencaoModel[]> {
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.get<OrionApiResponseModel<MaterialManutencaoModel[]>>(
|
||||||
|
`${this.Rota}/getMateriaisManutencao`
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
).then(materiais =>
|
||||||
|
materiais.map(material => ({
|
||||||
|
...material,
|
||||||
|
id: this.numero(material.id),
|
||||||
|
custo_padrao: this.numero(material.custo_padrao),
|
||||||
|
preco_venda_padrao: this.numero(material.preco_venda_padrao)
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// ORDENS DE SERVIÇO
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
getOrdensServico(
|
||||||
|
filtros: OrdemServicoFiltrosModel = {}
|
||||||
|
): Promise<OrdemServicoListaResponseModel> {
|
||||||
|
const params = this.montarParametros(filtros);
|
||||||
|
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.get<OrionApiResponseModel<OrdemServicoListaResponseModel>>(
|
||||||
|
`${this.Rota}/getOrdensServico`,
|
||||||
|
{ params }
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
).then(resultado => ({
|
||||||
|
items: resultado.items.map(item => this.normalizarOrdemLista(item)),
|
||||||
|
pagination: {
|
||||||
|
page: this.numero(resultado.pagination.page),
|
||||||
|
pageSize: this.numero(resultado.pagination.pageSize),
|
||||||
|
total: this.numero(resultado.pagination.total),
|
||||||
|
totalPages: this.numero(resultado.pagination.totalPages)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
getOrdemServico(id: number): Promise<OrdemServicoDetalhesModel> {
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.get<OrionApiResponseModel<OrdemServicoDetalhesModel>>(
|
||||||
|
`${this.Rota}/getOrdemServico/${id}`
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
).then(ordem => {
|
||||||
|
const normalizada = this.normalizarOrdemLista(ordem);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...ordem,
|
||||||
|
...normalizada,
|
||||||
|
custo_insumos: this.numero(ordem.custo_insumos),
|
||||||
|
atividades: (ordem.atividades || []).map(atividade => ({
|
||||||
|
...atividade,
|
||||||
|
id: this.numero(atividade.id),
|
||||||
|
ordem_servico_id: this.numero(atividade.ordem_servico_id),
|
||||||
|
ordem_exibicao: this.numero(atividade.ordem_exibicao),
|
||||||
|
horas_expediente: this.numero(atividade.horas_expediente),
|
||||||
|
horas_extras: this.numero(atividade.horas_extras),
|
||||||
|
valor_hora_expediente: this.numero(
|
||||||
|
atividade.valor_hora_expediente
|
||||||
|
),
|
||||||
|
valor_hora_extra: this.numero(atividade.valor_hora_extra),
|
||||||
|
valor_mao_obra: this.numero(atividade.valor_mao_obra),
|
||||||
|
tecnicos: atividade.tecnicos || [],
|
||||||
|
materiais: (atividade.materiais || []).map(material => ({
|
||||||
|
...material,
|
||||||
|
quantidade: this.numero(material.quantidade),
|
||||||
|
valor_custo_unitario: this.numero(
|
||||||
|
material.valor_custo_unitario
|
||||||
|
),
|
||||||
|
valor_cobrado_unitario: this.numero(
|
||||||
|
material.valor_cobrado_unitario
|
||||||
|
),
|
||||||
|
valor_custo_total: this.numero(material.valor_custo_total),
|
||||||
|
valor_cobrado_total: this.numero(
|
||||||
|
material.valor_cobrado_total
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
})),
|
||||||
|
odometros_da_os: ordem.odometros_da_os || [],
|
||||||
|
pagamentos: (ordem.pagamentos || []).map(pagamento => ({
|
||||||
|
...pagamento,
|
||||||
|
valor: this.numero(pagamento.valor)
|
||||||
|
})),
|
||||||
|
historico_status: ordem.historico_status || []
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
insertOrdemServico(
|
||||||
|
ordem: OrdemServicoSalvarModel
|
||||||
|
): Promise<OrdemServicoOperacaoResultModel> {
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.post<OrionApiResponseModel<OrdemServicoOperacaoResultModel>>(
|
||||||
|
`${this.Rota}/insertOrdemServico`,
|
||||||
|
ordem
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOrdemServico(
|
||||||
|
id: number,
|
||||||
|
ordem: OrdemServicoSalvarModel
|
||||||
|
): Promise<OrdemServicoOperacaoResultModel> {
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.put<OrionApiResponseModel<OrdemServicoOperacaoResultModel>>(
|
||||||
|
`${this.Rota}/updateOrdemServico/${id}`,
|
||||||
|
ordem
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteOrdemServico(
|
||||||
|
id: number,
|
||||||
|
usuarioId?: number
|
||||||
|
): Promise<OrdemServicoOperacaoResultModel> {
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.request<OrionApiResponseModel<OrdemServicoOperacaoResultModel>>(
|
||||||
|
'DELETE',
|
||||||
|
`${this.Rota}/deleteOrdemServico/${id}`,
|
||||||
|
{
|
||||||
|
body: {
|
||||||
|
usuario_id: usuarioId || null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// ATIVIDADES
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
insertAtividadeOrdemServico(
|
||||||
|
ordemServicoId: number,
|
||||||
|
atividade: OrdemServicoAtividadeSalvarModel
|
||||||
|
): Promise<OrdemServicoOperacaoResultModel> {
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.post<OrionApiResponseModel<OrdemServicoOperacaoResultModel>>(
|
||||||
|
`${this.Rota}/insertAtividadeOrdemServico/${ordemServicoId}`,
|
||||||
|
atividade
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAtividadeOrdemServico(
|
||||||
|
atividadeId: number,
|
||||||
|
atividade: OrdemServicoAtividadeSalvarModel
|
||||||
|
): Promise<OrdemServicoOperacaoResultModel> {
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.put<OrionApiResponseModel<OrdemServicoOperacaoResultModel>>(
|
||||||
|
`${this.Rota}/updateAtividadeOrdemServico/${atividadeId}`,
|
||||||
|
atividade
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAtividadeOrdemServico(
|
||||||
|
atividadeId: number
|
||||||
|
): Promise<OrdemServicoOperacaoResultModel> {
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.delete<OrionApiResponseModel<OrdemServicoOperacaoResultModel>>(
|
||||||
|
`${this.Rota}/deleteAtividadeOrdemServico/${atividadeId}`
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// ODÔMETROS
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
insertOdometroOrdemServico(
|
||||||
|
ordemServicoId: number,
|
||||||
|
odometro: OrdemServicoOdometroModel
|
||||||
|
): Promise<OrdemServicoOperacaoResultModel> {
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.post<OrionApiResponseModel<OrdemServicoOperacaoResultModel>>(
|
||||||
|
`${this.Rota}/insertOdometroOrdemServico/${ordemServicoId}`,
|
||||||
|
odometro
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// PAGAMENTOS
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
insertPagamentoOrdemServico(
|
||||||
|
ordemServicoId: number,
|
||||||
|
pagamento: OrdemServicoPagamentoModel
|
||||||
|
): Promise<OrdemServicoOperacaoResultModel> {
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.post<OrionApiResponseModel<OrdemServicoOperacaoResultModel>>(
|
||||||
|
`${this.Rota}/insertPagamentoOrdemServico/${ordemServicoId}`,
|
||||||
|
pagamento
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelPagamentoOrdemServico(
|
||||||
|
pagamentoId: number,
|
||||||
|
usuarioId?: number
|
||||||
|
): Promise<OrdemServicoOperacaoResultModel> {
|
||||||
|
return this.executar(
|
||||||
|
this.http
|
||||||
|
.request<OrionApiResponseModel<OrdemServicoOperacaoResultModel>>(
|
||||||
|
'DELETE',
|
||||||
|
`${this.Rota}/cancelPagamentoOrdemServico/${pagamentoId}`,
|
||||||
|
{
|
||||||
|
body: {
|
||||||
|
usuario_id: usuarioId || null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadOrdemServicoPdf(
|
||||||
|
id: number,
|
||||||
|
interno: boolean = false
|
||||||
|
): Promise<Blob> {
|
||||||
|
var url =
|
||||||
|
this.Rota +
|
||||||
|
'/downloadOrdemServicoPdf/' +
|
||||||
|
id +
|
||||||
|
'?interno=' +
|
||||||
|
(interno ? '1' : '0');
|
||||||
|
|
||||||
|
return this.http
|
||||||
|
.get(url, {
|
||||||
|
responseType: 'blob' as 'json'
|
||||||
|
})
|
||||||
|
.toPromise()
|
||||||
|
.then(response => response as Blob);
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadRelatorioOrdensServicoPdf(
|
||||||
|
filtros: any = {}
|
||||||
|
): Promise<Blob> {
|
||||||
|
var params = this.montarParametros(filtros);
|
||||||
|
|
||||||
|
return this.http
|
||||||
|
.get(
|
||||||
|
this.Rota + '/downloadRelatorioOrdensServicoPdf',
|
||||||
|
{
|
||||||
|
params: params,
|
||||||
|
responseType: 'blob' as 'json'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
.then(response => response as Blob);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue