Proceso basico de pedidos
This commit is contained in:
parent
065effae3d
commit
ab74433b70
@ -21,6 +21,41 @@ namespace WebVentaCoche.Controllers
|
||||
_context = context;
|
||||
}
|
||||
|
||||
//GET:/Account/Details/{id}
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Details(string id)
|
||||
{
|
||||
if (string.IsNullOrEmpty(id))
|
||||
return BadRequest();
|
||||
|
||||
var user = await _userHelper.GetUserByIdAsync(id);
|
||||
if (user == null)
|
||||
return NotFound();
|
||||
|
||||
var addresses = await _context.Addresses.Where(a => a.UserId == id).ToListAsync();
|
||||
|
||||
var vm = new UserDetailsViewModel
|
||||
{
|
||||
Id = user.Id,
|
||||
Name = user.Name,
|
||||
Surname = user.Surname,
|
||||
Email = user.Email,
|
||||
PhoneNumber = user.PhoneNumber,
|
||||
UserType = user.UserType,
|
||||
Addresses = addresses.Select(a => new AddressViewModel
|
||||
{
|
||||
Id = a.Id,
|
||||
Street = a.Street,
|
||||
City = a.City,
|
||||
State = a.State,
|
||||
ZipCode = a.ZipCode,
|
||||
Country = a.Country
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
//GET:/Account/Settings
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Settings()
|
||||
@ -29,9 +64,7 @@ namespace WebVentaCoche.Controllers
|
||||
var user = await _userHelper.GetUserByIdAsync(userId);
|
||||
if (user == null) return NotFound();
|
||||
|
||||
var addresses = await _context.Addresses
|
||||
.Where(a => a.UserId == userId)
|
||||
.ToListAsync();
|
||||
var addresses = await _context.Addresses.Where(a => a.UserId == userId).ToListAsync();
|
||||
|
||||
var vm = new UserDetailsViewModel
|
||||
{
|
||||
|
||||
@ -4,6 +4,8 @@ using WebVentaCoche.DataBase;
|
||||
using WebVentaCoche.Helpers;
|
||||
using WebVentaCoche.Models;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using WebVentaCoche.Enums;
|
||||
|
||||
namespace WebVentaCoche.Controllers
|
||||
{
|
||||
@ -17,7 +19,6 @@ namespace WebVentaCoche.Controllers
|
||||
}
|
||||
|
||||
//GET:/Cart/Index
|
||||
// Muestra la lista de productos del carrito y el total.
|
||||
public IActionResult Index()
|
||||
{
|
||||
// Diccionario de IDs -> Cantidades desde sesión (o cualquier fuente)
|
||||
@ -45,8 +46,63 @@ namespace WebVentaCoche.Controllers
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
// POST: /Cart/Purchase
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Purchase()
|
||||
{
|
||||
// 1) Obtén el carrito de la sesión
|
||||
var items = CartSessionHelper.GetCartItems(HttpContext.Session);
|
||||
if (!items.Any())
|
||||
{
|
||||
TempData["Error"] = "El carrito está vacío.";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier)!;
|
||||
|
||||
var addr = await _context.Addresses.Where(a => a.UserId == userId).FirstOrDefaultAsync();
|
||||
if (addr == null)
|
||||
{
|
||||
TempData["Error"] = "No tienes ninguna dirección guardada. Primero añade una dirección en tu perfil.";
|
||||
return RedirectToAction("Addresses", "Account");
|
||||
}
|
||||
var shippingAddress = $"{addr.Street}, {addr.City}, {addr.State}, {addr.ZipCode}, {addr.Country}";
|
||||
|
||||
var order = new Order
|
||||
{
|
||||
UserId = userId,
|
||||
OrderDate = DateTime.UtcNow,
|
||||
Status = OrderStatus.Pending,
|
||||
ShippingAddress = shippingAddress
|
||||
};
|
||||
|
||||
decimal total = 0m;
|
||||
foreach (var (productId, quantity) in items)
|
||||
{
|
||||
var product = await _context.Products.FindAsync(productId);
|
||||
if (product == null) continue;
|
||||
|
||||
var detail = new OrderDetail
|
||||
{
|
||||
ProductId = product.Id,
|
||||
Quantity = quantity,
|
||||
UnitPrice = product.Price
|
||||
};
|
||||
total += detail.Subtotal;
|
||||
order.OrderDetails.Add(detail);
|
||||
}
|
||||
|
||||
order.TotalAmount = total;
|
||||
_context.Orders.Add(order);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
CartSessionHelper.ClearCart(HttpContext.Session);
|
||||
|
||||
return RedirectToAction("Details", "Order", new { id = order.Id });
|
||||
}
|
||||
|
||||
//POST:/Cart/AddProductToCart
|
||||
// Añade el producto al carrito y devuelve un JSON con el nuevo contador
|
||||
[HttpPost]
|
||||
public IActionResult AddProductToCart(int id)
|
||||
{
|
||||
@ -67,7 +123,6 @@ namespace WebVentaCoche.Controllers
|
||||
}
|
||||
|
||||
//POST:/Cart/Remove
|
||||
// Elimina un producto según su ID
|
||||
[HttpPost]
|
||||
public IActionResult Remove(int id)
|
||||
{
|
||||
@ -76,7 +131,6 @@ namespace WebVentaCoche.Controllers
|
||||
}
|
||||
|
||||
//POST:/Cart/Clear
|
||||
// Vacía todo el carrito (la sesión)
|
||||
[HttpPost]
|
||||
public IActionResult Clear()
|
||||
{
|
||||
@ -85,7 +139,6 @@ namespace WebVentaCoche.Controllers
|
||||
}
|
||||
|
||||
//GET:/Cart/Checkout
|
||||
// (Opcional) Muestra un formulario o datos para la compra/pago.
|
||||
public IActionResult Checkout()
|
||||
{
|
||||
return View();
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Security.Claims;
|
||||
using WebVentaCoche.DataBase;
|
||||
using WebVentaCoche.Models;
|
||||
|
||||
namespace WebVentaCoche.Controllers
|
||||
{
|
||||
[Authorize] // Requiere autenticación para acceder al controlador
|
||||
[Authorize]
|
||||
public class OrderController : Controller
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
@ -19,28 +20,31 @@ namespace WebVentaCoche.Controllers
|
||||
//GET:Order/Index
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var orders = await _context.Orders
|
||||
.Include(o => o.User) // Cargar la relación con el usuario
|
||||
.ToListAsync();
|
||||
var orders = await _context.Orders.Include(o => o.User).ToListAsync();
|
||||
return View(orders);
|
||||
}
|
||||
//GET:/Order/MyOrders
|
||||
[Authorize]
|
||||
public async Task<IActionResult> UserOrders()
|
||||
{
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier)!;
|
||||
var myOrders = await _context.Orders.Where(o => o.UserId == userId).Include(o => o.OrderDetails).ThenInclude(d => d.Product).OrderByDescending(o => o.OrderDate).ToListAsync();
|
||||
|
||||
//GET:Order/Details/5
|
||||
return View(myOrders);
|
||||
}
|
||||
//GET:Order/Details/{id}
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Details(int? id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var order = await _context.Orders
|
||||
.Include(o => o.User) // Cargar la relación con el usuario
|
||||
.FirstOrDefaultAsync(o => o.Id == id);
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier)!;
|
||||
|
||||
var order = await _context.Orders.Include(o => o.OrderDetails).ThenInclude(d => d.Product).FirstOrDefaultAsync(o => o.Id == id && o.UserId == userId);
|
||||
|
||||
if (order == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return View(order);
|
||||
}
|
||||
|
||||
@ -2,10 +2,10 @@
|
||||
{
|
||||
public enum OrderStatus
|
||||
{
|
||||
Pending, // Orden pendiente
|
||||
Processing, // Orden en proceso
|
||||
Shipped, // Orden enviada
|
||||
Delivered, // Orden entregada
|
||||
Cancelled // Orden cancelada
|
||||
Pending,
|
||||
Processing,
|
||||
Shipped,
|
||||
Delivered,
|
||||
Cancelled
|
||||
}
|
||||
}
|
||||
|
||||
478
WebVentaCoche/Migrations/20250430123732_AddOrderAndDetails.Designer.cs
generated
Normal file
478
WebVentaCoche/Migrations/20250430123732_AddOrderAndDetails.Designer.cs
generated
Normal file
@ -0,0 +1,478 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using WebVentaCoche.DataBase;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace WebVentaCoche.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20250430123732_AddOrderAndDetails")]
|
||||
partial class AddOrderAndDetails
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex")
|
||||
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("WebVentaCoche.Models.Address", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("City")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Country")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("State")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Street")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ZipCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Addresses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("WebVentaCoche.Models.Order", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("OrderDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<DateTime?>("ShippedDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("ShippingAddress")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("TotalAmount")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Orders");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("WebVentaCoche.Models.OrderDetail", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("OrderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("ProductId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("UnitPrice")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OrderId");
|
||||
|
||||
b.HasIndex("ProductId");
|
||||
|
||||
b.ToTable("OrderDetails");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("WebVentaCoche.Models.Product", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ImagePath")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("LongDescription")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<decimal>("Price")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<string>("ShortDescription")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Products");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("WebVentaCoche.Models.User", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("datetimeoffset");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Surname")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<int>("UserType")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex")
|
||||
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("WebVentaCoche.Models.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("WebVentaCoche.Models.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("WebVentaCoche.Models.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("WebVentaCoche.Models.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("WebVentaCoche.Models.Address", b =>
|
||||
{
|
||||
b.HasOne("WebVentaCoche.Models.User", "User")
|
||||
.WithMany("Addresses")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("WebVentaCoche.Models.Order", b =>
|
||||
{
|
||||
b.HasOne("WebVentaCoche.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("WebVentaCoche.Models.OrderDetail", b =>
|
||||
{
|
||||
b.HasOne("WebVentaCoche.Models.Order", "Order")
|
||||
.WithMany("OrderDetails")
|
||||
.HasForeignKey("OrderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("WebVentaCoche.Models.Product", "Product")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProductId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Order");
|
||||
|
||||
b.Navigation("Product");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("WebVentaCoche.Models.Order", b =>
|
||||
{
|
||||
b.Navigation("OrderDetails");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("WebVentaCoche.Models.User", b =>
|
||||
{
|
||||
b.Navigation("Addresses");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace WebVentaCoche.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddOrderAndDetails : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,27 +9,19 @@ namespace WebVentaCoche.Models
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
public string UserId { get; set; }
|
||||
|
||||
[ForeignKey("UserId")]
|
||||
public virtual User User { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime OrderDate { get; set; } // Fecha y hora en que se realizó la orden
|
||||
|
||||
public DateTime? ShippedDate { get; set; } // Fecha de envío (opcional)
|
||||
|
||||
public DateTime OrderDate { get; set; }
|
||||
public DateTime? ShippedDate { get; set; }
|
||||
[Required]
|
||||
public decimal TotalAmount { get; set; } // Monto total de la orden
|
||||
|
||||
public decimal TotalAmount { get; set; }
|
||||
[Required]
|
||||
public OrderStatus Status { get; set; } // Estado de la orden (Pendiente, Enviado, Completado, Cancelado)
|
||||
|
||||
public OrderStatus Status { get; set; }
|
||||
[Required]
|
||||
public string ShippingAddress { get; set; } // Dirección de envío del pedido
|
||||
|
||||
public virtual List<OrderDetail> OrderDetails { get; set; } // Relación con los detalles de la orden
|
||||
public string ShippingAddress { get; set; }
|
||||
public virtual List<OrderDetail> OrderDetails { get; set; } = new List<OrderDetail>();
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,23 +7,15 @@ namespace WebVentaCoche.Models
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
public int OrderId { get; set; }
|
||||
|
||||
[ForeignKey("OrderId")]
|
||||
public virtual Order Order { get; set; } // Relación con la orden
|
||||
|
||||
[Required]
|
||||
public virtual Order Order { get; set; }
|
||||
public int ProductId { get; set; }
|
||||
|
||||
[ForeignKey("ProductId")]
|
||||
public virtual Product Product { get; set; } // Relación con el producto
|
||||
|
||||
[Required]
|
||||
public int Quantity { get; set; } // Cantidad del producto en la orden
|
||||
|
||||
[Required]
|
||||
public decimal UnitPrice { get; set; } // Precio unitario del producto en el momento de la compra
|
||||
public virtual Product Product { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public decimal UnitPrice { get; set; }
|
||||
public decimal Subtotal => Quantity * UnitPrice;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ namespace WebVentaCoche.Services
|
||||
|
||||
public EmailService(IConfiguration configuration)
|
||||
{
|
||||
// Leer configuraciones desde appsettings.json
|
||||
_apiKey = configuration["SendGrid:ApiKey"];
|
||||
_fromEmail = configuration["SendGrid:FromEmail"];
|
||||
_fromName = configuration["SendGrid:FromName"];
|
||||
@ -20,23 +19,17 @@ namespace WebVentaCoche.Services
|
||||
|
||||
public async Task SendEmailAsync(string to, string subject, string htmlContent)
|
||||
{
|
||||
// Crear cliente SendGrid
|
||||
var client = new SendGridClient(_apiKey);
|
||||
|
||||
// Configurar remitente y destinatario
|
||||
var from = new EmailAddress(_fromEmail, _fromName);
|
||||
var toEmail = new EmailAddress(to);
|
||||
|
||||
// Convertir el contenido HTML a texto plano eliminando etiquetas
|
||||
var plainTextContent = System.Text.RegularExpressions.Regex.Replace(htmlContent, "<[^>]+>", string.Empty);
|
||||
|
||||
// Crear el mensaje
|
||||
var message = MailHelper.CreateSingleEmail(from, toEmail, subject, plainTextContent, htmlContent);
|
||||
|
||||
// Enviar el mensaje
|
||||
var response = await client.SendEmailAsync(message);
|
||||
|
||||
// Manejar errores en caso de que el envío falle
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
|
||||
{
|
||||
throw new Exception("Error: Solicitud inválida al enviar el correo.");
|
||||
|
||||
10
WebVentaCoche/ViewModels/OrderSummaryViewModel.cs
Normal file
10
WebVentaCoche/ViewModels/OrderSummaryViewModel.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace WebVentaCoche.ViewModels
|
||||
{
|
||||
public class OrderSummaryViewModel
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public DateTime OrderDate { get; set; }
|
||||
public decimal TotalAmount { get; set; }
|
||||
public string Status { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
@ -88,9 +88,14 @@
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<a class="btn btn-primary ms-2" href="#">
|
||||
<form asp-action="Purchase" asp-controller="Cart" method="post" class="d-inline">
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-primary ms-2">
|
||||
Proceder al Pago
|
||||
</a>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -1,20 +1,60 @@
|
||||
@model WebVentaCoche.Models.Order
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Detalles de la Orden";
|
||||
ViewData["Title"] = "Detalle de Pedido";
|
||||
}
|
||||
|
||||
<div class="container mt-5">
|
||||
<h2 class="mb-4">Detalles de la Orden</h2>
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Orden #@Model.Id</h5>
|
||||
<p class="card-text"><strong>Fecha del Pedido:</strong> @Model.OrderDate.ToString("dd/MM/yyyy")</p>
|
||||
<p class="card-text"><strong>Cliente:</strong> @Model.User.Name @Model.User.Surname</p>
|
||||
<p class="card-text"><strong>Estado:</strong> @Model.Status</p>
|
||||
<p class="card-text"><strong>Total:</strong> @Model.TotalAmount.ToString("C")</p>
|
||||
<p class="card-text"><strong>Dirección de Envío:</strong> @Model.ShippingAddress</p>
|
||||
<a href="@Url.Action("Index", "Order")" class="btn btn-primary mt-3">Volver a la Lista</a>
|
||||
<div class="container mt-4">
|
||||
<h2 class="mb-4">Pedido #@Model.Id</h2>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<p><strong>Fecha del pedido:</strong> @Model.OrderDate.ToString("g")</p>
|
||||
<p><strong>Estado:</strong> @Model.Status</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p><strong>Dirección de envío:</strong><br />@Model.ShippingAddress</p>
|
||||
<p>
|
||||
<strong>Fecha de envío:</strong> @(Model.ShippedDate.HasValue ? Model.ShippedDate.Value.ToString("g") : "Pendiente")
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="mb-3">Productos del Pedido</h4>
|
||||
<table class="table table-striped align-middle">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th style="width:100px;">Imagen</th>
|
||||
<th>Nombre</th>
|
||||
<th>Cantidad</th>
|
||||
<th>Precio unidad</th>
|
||||
<th>Subtotal</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var detalle in Model.OrderDetails)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
@if (!string.IsNullOrEmpty(detalle.Product.ImagePath))
|
||||
{
|
||||
<img src="@detalle.Product.ImagePath" alt="@detalle.Product.Name" class="img-fluid rounded" style="max-height:80px;" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<img src="/images/default.png" alt="Sin imagen" class="img-fluid rounded" style="max-height:80px;" />
|
||||
}
|
||||
</td>
|
||||
<td>@detalle.Product.Name</td>
|
||||
<td>@detalle.Quantity</td>
|
||||
<td>@detalle.UnitPrice.ToString("0.00") €</td>
|
||||
<td>@detalle.Subtotal.ToString("0.00") €</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="text-end mt-4">
|
||||
<h4>Total del pedido: @Model.TotalAmount.ToString("0.00") €</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
40
WebVentaCoche/Views/Order/UserOrders.cshtml
Normal file
40
WebVentaCoche/Views/Order/UserOrders.cshtml
Normal file
@ -0,0 +1,40 @@
|
||||
@model IEnumerable<WebVentaCoche.Models.Order>
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Mis Pedidos";
|
||||
}
|
||||
|
||||
<div class="container mt-4">
|
||||
<h2 class="mb-4 text-center">Mis Pedidos</h2>
|
||||
|
||||
@if (!Model.Any())
|
||||
{
|
||||
<p class="text-center">Aún no has realizado ningún pedido.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row">
|
||||
@foreach (var order in Model)
|
||||
{
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">Pedido #@order.Id</h5>
|
||||
<p class="card-text mb-1"><strong>Fecha:</strong> @order.OrderDate.ToString("g")</p>
|
||||
<p class="card-text mb-1"><strong>Total:</strong> @order.TotalAmount.ToString("0.00") €</p>
|
||||
<p class="card-text mb-1"><strong>Estado:</strong> @order.Status</p>
|
||||
<p class="card-text mt-auto"><strong>Envío:</strong><br />@order.ShippingAddress</p>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent border-0 text-end">
|
||||
<a asp-action="Details"
|
||||
asp-route-id="@order.Id"
|
||||
class="btn btn-sm btn-primary">
|
||||
Ver detalles
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@ -76,7 +76,7 @@
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
|
||||
<li> <a class="dropdown-item" asp-controller="Account" asp-action="Details" asp-route-id="@userId">Detalles Cuenta</a></li>
|
||||
<li><a class="dropdown-item" asp-controller="Account" asp-action="Settings">Configuración</a></li>
|
||||
<li><a class="dropdown-item" href="@Url.Action("Index", "Order")">Pedidos</a></li>
|
||||
<li><a class="dropdown-item" asp-controller="Order" asp-action="UserOrders">Mis Pedidos</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<form method="post" asp-action="Logout" asp-controller="User" class="d-inline">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user