lots of changes

This commit is contained in:
2025-06-06 02:00:05 -05:00
parent 6cf2fdec61
commit 9daee50a3a
33 changed files with 1478 additions and 260 deletions

View File

View File

@ -0,0 +1,41 @@
from datetime import datetime
from app import db
class TransferRequest(db.Model):
__tablename__ = 'transfer_request'
__table_args__ = {'extend_existing': True}
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
plant_id = db.Column(db.Integer, db.ForeignKey('plant.id'), nullable=False)
seller_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
buyer_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
status = db.Column(
db.String(20),
nullable=False,
default='pending'
)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, onupdate=datetime.utcnow)
seller_message = db.Column(db.String(512), nullable=True)
buyer_message = db.Column(db.String(512), nullable=True)
plant = db.relationship(
'plugins.plant.models.Plant',
backref=db.backref('transfer_requests', lazy='dynamic'),
lazy=True
)
seller = db.relationship(
'plugins.auth.models.User',
foreign_keys=[seller_id],
backref='outgoing_transfers',
lazy=True
)
buyer = db.relationship(
'plugins.auth.models.User',
foreign_keys=[buyer_id],
backref='incoming_transfers',
lazy=True
)
def __repr__(self):
return f"<TransferRequest id={self.id} plant={self.plant_id} seller={self.seller_id} buyer={self.buyer_id} status={self.status}>"

View File

@ -0,0 +1,6 @@
{
"name": "transfer",
"version": "1.0.0",
"description": "Handles plant transfer requests between users",
"entry_point": ""
}

152
plugins/transfer/routes.py Normal file
View File

@ -0,0 +1,152 @@
from datetime import datetime
from flask import Blueprint, request, redirect, url_for, flash, render_template
from flask_login import login_required, current_user
from plugins.plant.models import db, Plant, PlantOwnershipLog
from plugins.transfer.models import TransferRequest
from plugins.auth.models import User
bp = Blueprint('transfer', __name__, template_folder='templates', url_prefix='/transfer')
@bp.route('/request/<int:plant_id>', methods=['GET', 'POST'])
@login_required
def request_transfer(plant_id):
plant = Plant.query.get_or_404(plant_id)
if plant.owner_id == current_user.id:
seller = current_user
if request.method == 'POST':
buyer_id = request.form.get('buyer_id', type=int)
buyer = User.query.get(buyer_id)
if not buyer or buyer.id == seller.id:
flash("Please select a valid buyer.", "error")
return redirect(request.url)
tr = TransferRequest(
plant_id=plant.id,
seller_id=seller.id,
buyer_id=buyer.id,
status='pending',
seller_message=request.form.get('seller_message', '').strip()
)
db.session.add(tr)
db.session.commit()
flash("Transfer request sent to buyer. Waiting for their approval.", "info")
return redirect(url_for('plant.view', plant_id=plant.id))
all_users = User.query.filter(User.id != seller.id).all()
return render_template(
'transfer/request_transfer.html',
plant=plant,
all_users=all_users
)
else:
buyer = current_user
if request.method == 'POST':
seller_id = request.form.get('seller_id', type=int)
seller = User.query.get(seller_id)
if not seller or seller.id != plant.owner_id:
flash("Please select the correct seller (current owner).", "error")
return redirect(request.url)
tr = TransferRequest(
plant_id=plant.id,
seller_id=seller.id,
buyer_id=buyer.id,
status='pending',
buyer_message=request.form.get('buyer_message', '').strip()
)
db.session.add(tr)
db.session.commit()
flash("Transfer request sent to seller. Waiting for their approval.", "info")
return redirect(url_for('plant.view', plant_id=plant.id))
return render_template(
'transfer/request_transfer.html',
plant=plant,
all_users=[User.query.get(plant.owner_id)]
)
@bp.route('/incoming', methods=['GET'])
@login_required
def incoming_requests():
pending = TransferRequest.query.filter_by(
buyer_id=current_user.id,
status='pending'
).all()
return render_template('transfer/incoming.html', pending=pending)
@bp.route('/approve/<int:request_id>', methods=['POST'])
@login_required
def approve_request(request_id):
tr = TransferRequest.query.get_or_404(request_id)
if current_user.id not in (tr.seller_id, tr.buyer_id):
flash("Youre not authorized to approve this transfer.", "error")
return redirect(url_for('transfer.incoming_requests'))
if current_user.id == tr.buyer_id:
tr.status = 'buyer_approved'
tr.buyer_message = request.form.get('message', tr.buyer_message)
else:
tr.status = 'seller_approved'
tr.seller_message = request.form.get('message', tr.seller_message)
tr.updated_at = datetime.utcnow()
db.session.commit()
flash("You have approved the transfer. Waiting on the other party.", "info")
return redirect(url_for('transfer.incoming_requests'))
@bp.route('/finalize/<int:request_id>', methods=['POST'])
@login_required
def finalize_request(request_id):
tr = TransferRequest.query.get_or_404(request_id)
buyer_approved = (
TransferRequest.query
.filter_by(id=tr.id, buyer_id=tr.buyer_id, status='buyer_approved')
.first() is not None
)
seller_approved = (
TransferRequest.query
.filter_by(id=tr.id, seller_id=tr.seller_id, status='seller_approved')
.first() is not None
)
if not (buyer_approved and seller_approved):
flash("Both parties must approve before finalizing.", "error")
return redirect(url_for('transfer.incoming_requests'))
new_log = PlantOwnershipLog(
plant_id=tr.plant_id,
user_id=tr.buyer_id,
date_acquired=datetime.utcnow(),
transferred=True,
is_verified=True
)
db.session.add(new_log)
plant = Plant.query.get(tr.plant_id)
plant.owner_id = tr.buyer_id
tr.status = 'complete'
tr.updated_at = datetime.utcnow()
db.session.commit()
flash("Transfer finalized—ownership updated.", "success")
return redirect(url_for('plant.view', plant_id=tr.plant_id))
@bp.route('/reject/<int:request_id>', methods=['POST'])
@login_required
def reject_request(request_id):
tr = TransferRequest.query.get_or_404(request_id)
if current_user.id not in (tr.seller_id, tr.buyer_id):
flash("Youre not authorized to reject this transfer.", "error")
return redirect(url_for('transfer.incoming_requests'))
tr.status = 'rejected'
tr.updated_at = datetime.utcnow()
db.session.commit()
flash("Transfer request has been rejected.", "warning")
return redirect(url_for('transfer.incoming_requests'))

View File

@ -0,0 +1,25 @@
{% extends 'core_ui/base.html' %}
{% block content %}
<h2>Incoming Transfer Requests</h2>
{% if pending %}
<ul>
{% for tr in pending %}
<li>
Plant: {{ tr.plant.custom_slug or tr.plant.uuid }} |
From: {{ tr.seller.username }} |
<form action="{{ url_for('transfer.approve_request', request_id=tr.id) }}" method="post" style="display:inline">
{% csrf_token %}
<button type="submit">Approve</button>
</form>
<form action="{{ url_for('transfer.reject_request', request_id=tr.id) }}" method="post" style="display:inline">
{% csrf_token %}
<button type="submit">Reject</button>
</form>
</li>
{% endfor %}
</ul>
{% else %}
<p>No pending requests.</p>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,28 @@
{% extends 'core_ui/base.html' %}
{% block content %}
<h2>Request Transfer: {{ plant.custom_slug or plant.uuid }}</h2>
<form method="post">
{% csrf_token %}
{% if plant.owner_id == current_user.id %}
<label for="buyer_id">Select Buyer:</label>
<select name="buyer_id" id="buyer_id">
{% for user in all_users %}
<option value="{{ user.id }}">{{ user.username }}</option>
{% endfor %}
</select>
<br>
<label for="seller_message">Message (optional):</label><br>
<textarea name="seller_message" id="seller_message"></textarea><br>
{% else %}
<label for="seller_id">Confirm Seller:</label>
<select name="seller_id" id="seller_id">
<option value="{{ all_users[0].id }}">{{ all_users[0].username }}</option>
</select>
<br>
<label for="buyer_message">Message (optional):</label><br>
<textarea name="buyer_message" id="buyer_message"></textarea><br>
{% endif %}
<button type="submit">Send Request</button>
</form>
{% endblock %}