feat: Implementar endpoint PUT para actualizar facturas draft, limitado a campos permitidos por Dolibarr (ref, notas, estado, fecha expiración) y unificar campo 'number' para consistencia
This commit is contained in:
parent
1fd331ac17
commit
a06ac2bdd9
|
|
@ -50,4 +50,16 @@ public class InvoicesController : ControllerBase
|
||||||
var invoiceId= await _invoiceService.CreateInvoiceAsync(createInvoiceDto);
|
var invoiceId= await _invoiceService.CreateInvoiceAsync(createInvoiceDto);
|
||||||
return CreatedAtAction(nameof(GetInvoice), new { id = invoiceId }, invoiceId);
|
return CreatedAtAction(nameof(GetInvoice), new { id = invoiceId }, invoiceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id:int}")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||||
|
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status403Forbidden)]
|
||||||
|
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||||
|
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)]
|
||||||
|
public async Task<IActionResult> UpdateInvoice([Range(1, int.MaxValue)] int id, [FromBody] UpdateInvoiceDto updateInvoiceDto)
|
||||||
|
{
|
||||||
|
await _invoiceService.UpdateInvoiceAsync(id, updateInvoiceDto);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -12,4 +12,7 @@ public class InvoiceResponse
|
||||||
public string? total_ttc { get; set; }
|
public string? total_ttc { get; set; }
|
||||||
public string? remaintopay { get; set; }
|
public string? remaintopay { get; set; }
|
||||||
public string? statut { get; set; }
|
public string? statut { get; set; }
|
||||||
|
|
||||||
|
public string? note_public { get; set; }
|
||||||
|
public string? note_private { get; set; }
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace DoliMiddlewareApi.Dtos.command;
|
||||||
|
|
||||||
|
public class UpdateInvoiceDto
|
||||||
|
{
|
||||||
|
// ClientId y Date no se cambian en PUT (Dolibarr no permite)
|
||||||
|
|
||||||
|
public DateTime? ExpireDate { get; set; }
|
||||||
|
|
||||||
|
[StringLength(100)]
|
||||||
|
public string? Number { get; set; } // Cambiado de Reference a Number para consistencia con GET
|
||||||
|
|
||||||
|
public string? NotePublic { get; set; }
|
||||||
|
public string? NotePrivate { get; set; }
|
||||||
|
|
||||||
|
public string Status { get; set; } = "draft";
|
||||||
|
|
||||||
|
// Lines quitadas - usa POST /lines para añadir líneas
|
||||||
|
}
|
||||||
|
|
@ -41,18 +41,14 @@ public class DolibarrApiClient
|
||||||
// porque devuelve el id como response
|
// porque devuelve el id como response
|
||||||
return await response.Content.ReadAsStringAsync();
|
return await response.Content.ReadAsStringAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<string> PutAsync(string endpoint, object requestBody)
|
||||||
|
{
|
||||||
|
var response = await _httpClient.PutAsJsonAsync(endpoint, requestBody);
|
||||||
|
await EnsureSuccessOrThrowAsync(response, endpoint);
|
||||||
|
|
||||||
|
return await response.Content.ReadAsStringAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ using System.Globalization;
|
||||||
using DoliMiddlewareApi.Dtos;
|
using DoliMiddlewareApi.Dtos;
|
||||||
using DoliMiddlewareApi.Dtos.command;
|
using DoliMiddlewareApi.Dtos.command;
|
||||||
using DoliMiddlewareApi.Dtos.Dolibarr;
|
using DoliMiddlewareApi.Dtos.Dolibarr;
|
||||||
|
using DoliMiddlewareApi.Exceptions;
|
||||||
using DoliMiddlewareApi.Mappers;
|
using DoliMiddlewareApi.Mappers;
|
||||||
using DoliMiddlewareApi.Services.Clients;
|
using DoliMiddlewareApi.Services.Clients;
|
||||||
|
|
||||||
|
|
@ -66,4 +67,22 @@ public class InvoiceService
|
||||||
|
|
||||||
return int.Parse(responseBody);
|
return int.Parse(responseBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task UpdateInvoiceAsync(int id, UpdateInvoiceDto dto)
|
||||||
|
{
|
||||||
|
// GET el JSON completo
|
||||||
|
var current = await _apiClient.GetResourceAsync<InvoiceDetailResponse>($"invoices/{id}");
|
||||||
|
if (current.statut != "0") throw new ForbiddenException("Solo drafts");
|
||||||
|
|
||||||
|
// Modifica solo campos que Dolibarr permite en PUT
|
||||||
|
if (dto.Number != null) current.@ref = dto.Number;
|
||||||
|
if (dto.NotePublic != null) current.note_public = dto.NotePublic;
|
||||||
|
if (dto.NotePrivate != null) current.note_private = dto.NotePrivate;
|
||||||
|
current.statut = dto.Status == "unpaid" ? "1" : "0";
|
||||||
|
if (dto.ExpireDate.HasValue) current.date_lim_reglement = ((DateTimeOffset)dto.ExpireDate.Value).ToUnixTimeSeconds();
|
||||||
|
|
||||||
|
// No tocar: date, socid, lines (Dolibarr no los cambia en PUT)
|
||||||
|
|
||||||
|
await _apiClient.PutAsync($"invoices/{id}", current);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🚀 Generando openapi.json..."
|
||||||
|
|
||||||
|
# Variables
|
||||||
|
PORT=5123
|
||||||
|
PROJECT_DIR="DoliMiddlewareApi"
|
||||||
|
OUTPUT_FILE="openapi.json"
|
||||||
|
|
||||||
|
# Levantar la API en background
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
dotnet run --urls "http://localhost:$PORT" > /dev/null 2>&1 &
|
||||||
|
PID=$!
|
||||||
|
|
||||||
|
# Esperar a que arranque
|
||||||
|
echo "⏳ Esperando a que la API arranque..."
|
||||||
|
sleep 8
|
||||||
|
|
||||||
|
# Descargar el OpenAPI spec
|
||||||
|
echo "📥 Descargando openapi.json..."
|
||||||
|
curl -s "http://localhost:$PORT/openapi/v1.json" -o "../$OUTPUT_FILE"
|
||||||
|
|
||||||
|
# Matar el proceso
|
||||||
|
kill $PID 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "✅ openapi.json generado exitosamente en: $OUTPUT_FILE"
|
||||||
|
echo ""
|
||||||
|
echo "📦 Para generar cliente TypeScript:"
|
||||||
|
echo " npx @openapitools/openapi-generator-cli generate -i openapi.json -g typescript-fetch -o ./client"
|
||||||
|
echo ""
|
||||||
|
echo "📦 Para importar en Postman:"
|
||||||
|
echo " File > Import > Selecciona openapi.json"
|
||||||
|
|
@ -0,0 +1,333 @@
|
||||||
|
{
|
||||||
|
"openapi": "3.0.4",
|
||||||
|
"info": {
|
||||||
|
"title": "DoliMiddlewareApi",
|
||||||
|
"version": "1.0"
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"/api/Invoices": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Invoices"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "limit",
|
||||||
|
"in": "query",
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32",
|
||||||
|
"default": 50
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "page",
|
||||||
|
"in": "query",
|
||||||
|
"schema": {
|
||||||
|
"maximum": 2147483647,
|
||||||
|
"minimum": 1,
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32",
|
||||||
|
"default": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "status",
|
||||||
|
"in": "query",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"content": {
|
||||||
|
"text/plain": {
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/InvoiceDto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/InvoiceDto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"text/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/InvoiceDto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"content": {
|
||||||
|
"text/plain": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProblemDetails"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProblemDetails"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"text/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProblemDetails"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/Invoices/{id}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Invoices"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"content": {
|
||||||
|
"text/plain": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/InvoiceDetailDto"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/InvoiceDetailDto"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"text/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/InvoiceDetailDto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"content": {
|
||||||
|
"text/plain": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProblemDetails"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProblemDetails"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"text/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProblemDetails"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"content": {
|
||||||
|
"text/plain": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProblemDetails"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProblemDetails"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"text/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProblemDetails"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"InvoiceDetailDto": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"number": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"expireDate": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"clientId": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"remainToPay": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"lines": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/InvoiceLineDto"
|
||||||
|
},
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"InvoiceDto": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"number": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"expireDate": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"clientId": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"remainToPay": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"InvoiceLineDto": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"quantity": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
"unitPrice": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
"taxRate": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"ProblemDetails": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"detail": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"instance": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"name": "Invoices"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue