Spaces:
Sleeping
Sleeping
Update main.py
Browse files
main.py
CHANGED
|
@@ -673,14 +673,14 @@ def propose_deal():
|
|
| 673 |
|
| 674 |
@app.route('/api/deals/<deal_id>/respond', methods=['POST'])
|
| 675 |
def respond_to_deal(deal_id):
|
| 676 |
-
auth_header = request.headers.get('Authorization')
|
| 677 |
-
uid = None
|
| 678 |
try:
|
| 679 |
uid = verify_token(auth_header)
|
| 680 |
if not FIREBASE_INITIALIZED: return jsonify({'error': 'Server configuration error'}), 503
|
| 681 |
|
| 682 |
data = request.get_json()
|
| 683 |
-
response_action = data.get('action')
|
| 684 |
|
| 685 |
if response_action not in ['accept', 'reject']:
|
| 686 |
return jsonify({'error': 'Invalid action. Must be "accept" or "reject".'}), 400
|
|
@@ -690,58 +690,105 @@ def respond_to_deal(deal_id):
|
|
| 690 |
|
| 691 |
if not deal_data or not isinstance(deal_data, dict):
|
| 692 |
return jsonify({'error': 'Deal not found.'}), 404
|
| 693 |
-
|
| 694 |
-
|
| 695 |
-
|
| 696 |
-
|
| 697 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 698 |
|
| 699 |
update_time = datetime.now(timezone.utc).isoformat()
|
| 700 |
-
buyer_id = deal_data.get('buyer_id')
|
| 701 |
-
listing_id = deal_data.get('listing_id')
|
| 702 |
|
| 703 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 704 |
if listing_id:
|
| 705 |
listing_data_for_notif = db.reference(f'listings/{listing_id}', app=db_app).get() or {}
|
| 706 |
-
crop_type_for_notif = listing_data_for_notif.get('crop_type', 'your listing')
|
| 707 |
-
|
| 708 |
|
| 709 |
if response_action == 'accept':
|
| 710 |
-
|
| 711 |
-
|
| 712 |
-
|
| 713 |
-
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
|
| 718 |
-
|
| 719 |
-
|
| 720 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 721 |
|
| 722 |
-
|
| 723 |
-
|
| 724 |
-
|
| 725 |
-
|
| 726 |
-
|
| 727 |
-
"
|
| 728 |
-
|
| 729 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 730 |
admins_ref = db.reference('users', app=db_app).order_by_child('is_admin').equal_to(True).get()
|
| 731 |
if admins_ref:
|
| 732 |
for admin_id_loop, _ in admins_ref.items():
|
| 733 |
-
_send_system_notification(admin_id_loop, f"Deal ID {deal_id} for '{crop_type_for_notif}' has been accepted by
|
| 734 |
-
|
|
|
|
| 735 |
|
| 736 |
elif response_action == 'reject':
|
| 737 |
-
|
| 738 |
-
|
| 739 |
-
|
| 740 |
-
|
| 741 |
-
"
|
| 742 |
-
f"
|
| 743 |
-
|
| 744 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 745 |
|
| 746 |
except Exception as e:
|
| 747 |
return handle_route_errors(e, uid_context=uid)
|
|
|
|
| 673 |
|
| 674 |
@app.route('/api/deals/<deal_id>/respond', methods=['POST'])
|
| 675 |
def respond_to_deal(deal_id):
|
| 676 |
+
auth_header = request.headers.get('Authorization')
|
| 677 |
+
uid = None # UID of the currently logged-in user responding
|
| 678 |
try:
|
| 679 |
uid = verify_token(auth_header)
|
| 680 |
if not FIREBASE_INITIALIZED: return jsonify({'error': 'Server configuration error'}), 503
|
| 681 |
|
| 682 |
data = request.get_json()
|
| 683 |
+
response_action = data.get('action') # 'accept' or 'reject'
|
| 684 |
|
| 685 |
if response_action not in ['accept', 'reject']:
|
| 686 |
return jsonify({'error': 'Invalid action. Must be "accept" or "reject".'}), 400
|
|
|
|
| 690 |
|
| 691 |
if not deal_data or not isinstance(deal_data, dict):
|
| 692 |
return jsonify({'error': 'Deal not found.'}), 404
|
| 693 |
+
|
| 694 |
+
current_deal_status = deal_data.get('status')
|
| 695 |
+
proposer_id = deal_data.get('proposer_id')
|
| 696 |
+
farmer_id_in_deal = deal_data.get('farmer_id')
|
| 697 |
+
buyer_id_in_deal = deal_data.get('buyer_id')
|
| 698 |
+
|
| 699 |
+
# Authorization Check: Who is allowed to respond?
|
| 700 |
+
can_respond = False
|
| 701 |
+
if current_deal_status == 'proposed':
|
| 702 |
+
if proposer_id == buyer_id_in_deal and uid == farmer_id_in_deal: # Buyer proposed, Farmer responds
|
| 703 |
+
can_respond = True
|
| 704 |
+
elif proposer_id == farmer_id_in_deal and uid == buyer_id_in_deal: # Farmer proposed/countered, Buyer responds
|
| 705 |
+
can_respond = True
|
| 706 |
+
# Add other statuses if a different party needs to respond (e.g., after admin action)
|
| 707 |
+
|
| 708 |
+
if not can_respond:
|
| 709 |
+
logger.warning(f"UID {uid} unauthorized to respond to deal {deal_id}. Deal status: {current_deal_status}, Proposer: {proposer_id}, Farmer: {farmer_id_in_deal}, Buyer: {buyer_id_in_deal}")
|
| 710 |
+
return jsonify({'error': 'Not authorized to respond to this deal at its current state.'}), 403
|
| 711 |
|
| 712 |
update_time = datetime.now(timezone.utc).isoformat()
|
|
|
|
|
|
|
| 713 |
|
| 714 |
+
# Determine the other party for notification
|
| 715 |
+
other_party_id = None
|
| 716 |
+
if uid == farmer_id_in_deal:
|
| 717 |
+
other_party_id = buyer_id_in_deal
|
| 718 |
+
elif uid == buyer_id_in_deal:
|
| 719 |
+
other_party_id = farmer_id_in_deal
|
| 720 |
+
|
| 721 |
+
listing_id = deal_data.get('listing_id')
|
| 722 |
+
listing_data_for_notif = {}
|
| 723 |
if listing_id:
|
| 724 |
listing_data_for_notif = db.reference(f'listings/{listing_id}', app=db_app).get() or {}
|
| 725 |
+
crop_type_for_notif = listing_data_for_notif.get('crop_type', 'your listing/demand')
|
|
|
|
| 726 |
|
| 727 |
if response_action == 'accept':
|
| 728 |
+
# Quantity check (if applicable, e.g., if responding to a proposal against a produce listing)
|
| 729 |
+
if proposer_id == buyer_id_in_deal and uid == farmer_id_in_deal: # Farmer accepting buyer's proposal
|
| 730 |
+
if listing_id and isinstance(listing_data_for_notif, dict):
|
| 731 |
+
available_quantity = listing_data_for_notif.get('quantity', 0)
|
| 732 |
+
proposed_quantity = deal_data.get('proposed_quantity', 0)
|
| 733 |
+
if proposed_quantity > available_quantity:
|
| 734 |
+
deal_ref.update({
|
| 735 |
+
'status': 'rejected_by_system_insufficient_qty',
|
| 736 |
+
'system_rejection_at': update_time,
|
| 737 |
+
'system_rejection_reason': f'Listing quantity ({available_quantity}) insufficient for deal quantity ({proposed_quantity}) at time of acceptance.'
|
| 738 |
+
})
|
| 739 |
+
if other_party_id:
|
| 740 |
+
_send_system_notification(other_party_id, f"Your deal proposal for '{crop_type_for_notif}' could not be accepted due to insufficient stock.", "deal_status_update", f"/deals/{deal_id}")
|
| 741 |
+
return jsonify({'success': False, 'error': 'Deal could not be accepted. Listing quantity is no longer sufficient.'}), 409
|
| 742 |
+
|
| 743 |
+
# If a buyer accepts a farmer's counter-offer, or farmer accepts buyer's initial proposal
|
| 744 |
+
# The deal moves to 'accepted_by_farmer' or 'accepted_by_buyer' then to admin.
|
| 745 |
+
# For simplicity, let's assume both lead to a state needing admin approval.
|
| 746 |
+
# We need a clear "who accepted" field.
|
| 747 |
|
| 748 |
+
accepted_by_field = ""
|
| 749 |
+
new_status = ""
|
| 750 |
+
if uid == farmer_id_in_deal:
|
| 751 |
+
accepted_by_field = "farmer_accepted_at"
|
| 752 |
+
new_status = "accepted_by_farmer" # Farmer accepts buyer's proposal
|
| 753 |
+
notification_message_to_other_party = f"Your deal proposal for '{crop_type_for_notif}' has been ACCEPTED by the farmer. It is now pending admin approval."
|
| 754 |
+
elif uid == buyer_id_in_deal:
|
| 755 |
+
accepted_by_field = "buyer_accepted_at"
|
| 756 |
+
new_status = "accepted_by_buyer" # Buyer accepts farmer's offer/counter
|
| 757 |
+
notification_message_to_other_party = f"Your offer/counter-offer for '{crop_type_for_notif}' has been ACCEPTED by the buyer. It is now pending admin approval."
|
| 758 |
+
else: # Should not happen due to auth check
|
| 759 |
+
return jsonify({'error': 'Internal error determining accepter role.'}), 500
|
| 760 |
+
|
| 761 |
+
deal_ref.update({'status': new_status, accepted_by_field: update_time, 'last_responder_id': uid})
|
| 762 |
+
|
| 763 |
+
if other_party_id:
|
| 764 |
+
_send_system_notification(other_party_id, notification_message_to_other_party, "deal_status_update", f"/deals/{deal_id}")
|
| 765 |
+
|
| 766 |
+
# Notify admins
|
| 767 |
admins_ref = db.reference('users', app=db_app).order_by_child('is_admin').equal_to(True).get()
|
| 768 |
if admins_ref:
|
| 769 |
for admin_id_loop, _ in admins_ref.items():
|
| 770 |
+
_send_system_notification(admin_id_loop, f"Deal ID {deal_id} for '{crop_type_for_notif}' has been accepted by {uid[:6]}... and needs your approval.", "admin_deal_approval_needed", f"/admin/deals/pending") # Path for admin to see pending deals
|
| 771 |
+
|
| 772 |
+
return jsonify({'success': True, 'message': f'Deal accepted by {("farmer" if uid == farmer_id_in_deal else "buyer")}, pending admin approval.'}), 200
|
| 773 |
|
| 774 |
elif response_action == 'reject':
|
| 775 |
+
rejected_by_field = ""
|
| 776 |
+
new_status = ""
|
| 777 |
+
if uid == farmer_id_in_deal:
|
| 778 |
+
rejected_by_field = "farmer_rejected_at" # Or just 'responded_at'
|
| 779 |
+
new_status = "rejected_by_farmer"
|
| 780 |
+
notification_message_to_other_party = f"Your deal proposal for '{crop_type_for_notif}' has been REJECTED by the farmer."
|
| 781 |
+
elif uid == buyer_id_in_deal:
|
| 782 |
+
rejected_by_field = "buyer_rejected_at"
|
| 783 |
+
new_status = "rejected_by_buyer"
|
| 784 |
+
notification_message_to_other_party = f"Your offer/counter-offer for '{crop_type_for_notif}' has been REJECTED by the buyer."
|
| 785 |
+
else: # Should not happen
|
| 786 |
+
return jsonify({'error': 'Internal error determining rejector role.'}), 500
|
| 787 |
+
|
| 788 |
+
deal_ref.update({'status': new_status, rejected_by_field: update_time, 'last_responder_id': uid})
|
| 789 |
+
if other_party_id:
|
| 790 |
+
_send_system_notification(other_party_id, notification_message_to_other_party, "deal_status_update", f"/deals/{deal_id}")
|
| 791 |
+
return jsonify({'success': True, 'message': f'Deal rejected by {("farmer" if uid == farmer_id_in_deal else "buyer")}.'}), 200
|
| 792 |
|
| 793 |
except Exception as e:
|
| 794 |
return handle_route_errors(e, uid_context=uid)
|