Skip to main content

Overview

The Wood Types catalog stores different types of wood materials available for furniture manufacturing. Each entry includes a name and optional description.

Data Model

The WoodType model is defined in app/models/wood_type.py:5:
class WoodType(db.Model):
    __tablename__ = 'wood_types'
    
    id_wood_type = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False, unique=True)
    description = db.Column(db.String(255), nullable=True)
    active = db.Column(db.Boolean, nullable=False, default=True)
    
    created_at = db.Column(db.TIMESTAMP, server_default=func.current_timestamp())
    updated_at = db.Column(db.TIMESTAMP, server_onupdate=func.current_timestamp())
    deleted_at = db.Column(db.TIMESTAMP, nullable=True)
    
    created_by = db.Column(db.String(100), nullable=True)
    updated_by = db.Column(db.String(100), nullable=True)
    deleted_by = db.Column(db.String(100), nullable=True)

Fields

FieldTypeDescription
id_wood_typeIntegerPrimary key
nameString(100)Unique wood type name
descriptionString(255)Optional description
activeBooleanSoft delete flag
created_atTimestampRecord creation date
updated_atTimestampLast modification date
deleted_atTimestampLogical deletion date
Unlike the Colors catalog, Wood Types include an optional description field for additional material details.

Service Layer

The WoodTypeService class in app/catalogs/wood_types/services.py:11 provides business logic:

Get All Wood Types

Retrieves all active wood types:
wood_types = WoodTypeService.get_all()
Implementation from services.py:15:
@staticmethod
def get_all() -> list[WoodType]:
    return WoodType.query.filter_by(active=True).all()

Create Wood Type

Creates a new wood type with name and optional description:
data = {
    "name": "Cedar",
    "description": "Aromatic softwood ideal for closets"
}
wood_type = WoodTypeService.create(data)
Implementation from services.py:25:
@staticmethod
def create(data: dict) -> dict:
    name = data.get("name")
    description = data.get("description")
    
    if not name or not name.strip():
        raise ValidationError("El nombre del tipo de madera es requerido")
    
    name = name.strip()
    
    existing = WoodType.query.filter_by(name=name).first()
    if existing:
        raise ConflictError(f"Ya existe un tipo de madera con el nombre '{name}'")
    
    wood_type = WoodType(name=name, description=description)
    db.session.add(wood_type)
    db.session.commit()
    
    return wood_type.to_dict()

Get Wood Type by ID

Retrieves a specific wood type:
wood_type = WoodTypeService.get_by_id(id_wood_type)
Implementation from services.py:63:
@staticmethod
def get_by_id(id_wood_type: int) -> WoodType:
    wood_type = WoodType.query.get(id_wood_type)
    if not wood_type:
        raise NotFoundError(f"No se encontró el tipo de madera con ID {id_wood_type}")
    return wood_type

Update Wood Type

Updates an existing wood type:
data = {
    "name": "Red Cedar",
    "description": "Premium aromatic cedar wood"
}
WoodTypeService.update(id_wood_type, data)
Implementation from services.py:82:
@staticmethod
def update(id_wood_type: int, data: dict) -> dict:
    wood_type = WoodType.query.get(id_wood_type)
    if not wood_type or not wood_type.active:
        raise NotFoundError(f"No se encontró el tipo de madera con ID {id_wood_type}")
    
    name = data.get("name")
    description = data.get("description")
    
    if not name or not name.strip():
        raise ValidationError("El nombre del tipo de madera es requerido")
    
    name = name.strip()
    
    existing = WoodType.query.filter(
        WoodType.name == name, 
        WoodType.id_wood_type != id_wood_type
    ).first()
    if existing:
        raise ConflictError(f"Ya existe otro tipo de madera con el nombre '{name}'")
    
    wood_type.name = name
    wood_type.description = description
    db.session.commit()
    
    return wood_type.to_dict()

Delete Wood Type

Performs a soft delete:
WoodTypeService.delete(id_wood_type)
Implementation from services.py:126:
@staticmethod
def delete(id_wood_type: int) -> None:
    wood_type = WoodType.query.get(id_wood_type)
    if not wood_type or not wood_type.active:
        raise NotFoundError(f"No se encontró el tipo de madera con ID {id_wood_type}")
    
    wood_type.active = False
    db.session.commit()

Routes

The routes module (app/catalogs/wood_types/routes.py) defines HTTP endpoints:

List Wood Types

Endpoint: GET /wood-types/
@woods_types_bp.route("/", methods=["GET"])
def list_wood_types():
    wood_types = WoodTypeService.get_all()
    return render_template("wood_types/list.html", wood_types=wood_types)

Create Wood Type

Endpoint: GET/POST /wood-types/create From routes.py:22:
@woods_types_bp.route("/create", methods=["GET", "POST"])
def create_wood_type():
    form = WoodTypeForm()
    
    if form.validate_on_submit():
        data = {
            "name": form.name.data,
            "description": form.description.data
        }
        try:
            WoodTypeService.create(data)
            flash("Tipo de madera creado exitosamente", "success")
            return redirect(url_for("woods_types.create_wood_type"))
        except ConflictError as e:
            flash(e.message, "error")
    
    return render_template("wood_types/create.html", form=form)

Edit Wood Type

Endpoint: GET/POST /wood-types/<id_wood_type>/edit From routes.py:49:
@woods_types_bp.route("/<int:id_wood_type>/edit", methods=["GET", "POST"])
def edit_wood_type(id_wood_type: int):
    form = WoodTypeForm()
    
    if form.validate_on_submit():
        data = {
            "name": form.name.data,
            "description": form.description.data
        }
        try:
            WoodTypeService.update(id_wood_type, data)
            flash("Tipo de madera actualizado exitosamente", "success")
            return redirect(url_for("woods_types.edit_wood_type", id_wood_type=id_wood_type))
        except ConflictError as e:
            flash(e.message, "error")
    
    wood_type = WoodTypeService.get_by_id(id_wood_type)
    if not wood_type:
        flash("Tipo de madera no encontrado", "error")
        return redirect(url_for("woods_types.list_wood_types"))
    
    form.name.data = wood_type.name
    form.description.data = wood_type.description
    
    return render_template("wood_types/edit.html", form=form, wood_type=wood_type)

Delete Wood Type

Endpoint: POST /wood-types/<id_wood_type>/delete From routes.py:86:
@woods_types_bp.route("/<int:id_wood_type>/delete", methods=["POST"])
def delete_wood_type(id_wood_type: int):
    try:
        WoodTypeService.delete(id_wood_type)
        flash("Tipo de madera eliminado exitosamente", "success")
    except Exception as e:
        flash(str(e), "error")
    
    return redirect(url_for("woods_types.list_wood_types"))

Form Definition

The WoodTypeForm class in app/catalogs/wood_types/forms.py:10 defines the input form:
class WoodTypeForm(FlaskForm):
    name = StringField(
        "Nombre",
        validators=[
            DataRequired(message="El nombre del tipo de madera es requerido"),
            Length(min=3, max=50, message="El nombre no puede exceder 50 caracteres"),
        ],
    )
    description = StringField(
        "Descripción",
        validators=[
            Length(max=200, message="La descripción no puede exceder 200 caracteres"),
        ],
    )

Validators

FieldValidators
nameDataRequired, Length(min=3, max=50)
descriptionLength(max=200) - Optional
The description field is optional and has no DataRequired validator.

Usage Examples

Creating a Wood Type

from app.catalogs.wood_types.services import WoodTypeService

# Create with description
wood_data = {
    "name": "Oak",
    "description": "Strong hardwood with prominent grain"
}
result = WoodTypeService.create(wood_data)

# Create without description
wood_data = {"name": "Pine"}
result = WoodTypeService.create(wood_data)

Common Wood Types

Typical entries in a furniture manufacturing system:

Hardwoods

  • Oak (Roble)
  • Walnut (Nogal)
  • Maple (Arce)
  • Cherry (Cerezo)

Softwoods

  • Pine (Pino)
  • Cedar (Cedro)
  • Fir (Abeto)
  • Spruce (Picea)

Listing Wood Types

# Get all active wood types
wood_types = WoodTypeService.get_all()
for wood in wood_types:
    print(f"{wood.name}: {wood.description}")

Best Practices

1

Use Standard Names

Use common wood species names (e.g., “Oak”, “Pine”, “Cedar”)
2

Add Descriptions

Provide helpful descriptions to distinguish similar wood types
3

Validate Before Creating

Check for duplicates before attempting to create new entries
4

Handle Exceptions

Always catch ConflictError and ValidationError exceptions

Build docs developers (and LLMs) love