I am trying to create an invoice/PO Form and need some help. I am using 4 models: Client, Shipping, Billing, and PO models. I have the OneToOne relationship between shipping and billing models to the client model and both as foreign keys (please let me know if this method is still ok) in PO model.
models.py ClientBillingAddress and ShippingAddress are identical, except "billing" is replaced with "shipping"
class ClientBillingAddress(models.Model):
client = models.OneToOneField(Client, on_delete=models.CASCADE, related_name='billing_address')
billing_location_name = models.CharField(max_length=255, unique=True)
address_line_1 = models.CharField(max_length=200, blank=True, null=True)
address_line_2 = models.CharField(max_length=100, blank=True, null=True)
address_city = models.CharField(max_length=50, blank=True, null=True)
address_province = models.CharField(max_length=50, blank=True, null=True)
address_postal = models.CharField(max_length=15, blank=True, null=True)
address_country = models.CharField(max_length=50, blank=True, null=True)
phone = models.CharField(max_length=15, null=True, blank=True)
email_to = models.CharField(max_length=150, null=True, blank=True)
email_cc = models.CharField(max_length=150, null=True, blank=True)
bill_contact_name = models.CharField(null=True, blank=True, max_length=50)
is_default = models.BooleanField(default=False)
def get_formatted_address(self):
address_lines = [self.billing_location_name, self.address_line_1, self.address_line_2]
address_city_info = f"{self.address_city}, {self.address_province}, {self.address_postal}"
address_lines.append(address_city_info)
address_lines.append(self.address_country)
return "\n".join(address_lines)
def __str__(self):
return self.billing_location_name
class ClientPO(models.Model):
client_PO_number = models.CharField(max_length=50, unique=True)
customer_company = models.ForeignKey(Client, on_delete=models.CASCADE, related_name="customer_name_PO")
order_reserved_total = models.PositiveBigIntegerField(default=0)
order_total = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, default=Decimal("0.00"))
order_line = models.PositiveBigIntegerField(default=0)
order_product = models.ForeignKey(InventoryItemDetail, on_delete=models.CASCADE, related_name="order_products")
order_qty = models.PositiveBigIntegerField(default=0)
order_unit_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, default=Decimal("0.00"))
order_extended_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, default=Decimal("0.00"))
order_units = models.CharField(max_length=10, choices=UNIT_TYPES, default='EA')
order_ship_to = models.ForeignKey(ClientShippingAddress, on_delete=models.CASCADE, related_name="order_shipto")
order_bill_to = models.ForeignKey(ClientBillingAddress, on_delete=models.CASCADE, related_name="order_billto")
order_open_qty = models.PositiveBigIntegerField(default=0)
order_shipped_qty = models.PositiveBigIntegerField(default=0)
order_status = models.CharField(max_length=2, choices=ORDER_STATUS_CHOICES, default='PN')
order_delivery_due = models.DateTimeField(auto_now=False)
date_created = models.DateTimeField(auto_now_add=True)
last_updated = models.DateTimeField(auto_now=True)
def calculate_extended_value(self):
# Calculate total value based on the item cost and quantity
order_extended_price = self.order_unit_price * self.order_qty
return order_extended_price
def __str__(self):
return self.client_PO_number
forms.py:
class ClientPOForm(forms.ModelForm):
billing_address = forms.CharField(
widget=forms.Textarea(attrs={
'class': 'form-textarea w-full resize-none text-xs rounded-lg border border-slate-300 bg-transparent p-2.5 placeholder:text-slate-400/70 hover:border-slate-400 focus:border-primary dark:border-navy-450 dark:hover:border-navy-400 dark:focus:border-accent',
'readonly': 'readonly',
})
)
shipping_address = forms.CharField(
widget=forms.Textarea(attrs={
'class': 'form-textarea w-full resize-none text-xs rounded-lg border border-slate-300 bg-transparent p-2.5 placeholder:text-slate-400/70 hover:border-slate-400 focus:border-primary dark:border-navy-450 dark:hover:border-navy-400 dark:focus:border-accent',
'readonly': 'readonly',
})
)
class Meta:
model = ClientPO
fields = [
'client_PO_number', 'order_product','order_qty','order_unit_price','order_units',
'order_ship_to','order_bill_to','order_delivery_due','customer_company'
]
views.py
def customer_add_purchase_order(request):
if request.method == 'POST':
customer_po_form = ClientPOForm(request.POST)
if customer_po_form.is_valid():
customer_po = customer_po_form.save(commit=False)
customer_po.customer_company = customer_po_form.cleaned_data['customer_company']
customer_po.save()
customer_po.slug = slugify(customer_po.client_PO_number)
customer_po.save()
messages.success(request, "Customer PO added successfully.")
return redirect('customer_orders')
else:
print(customer_po_form.errors)
else:
customer_po_form = ClientPOForm()
context = {
'customer_po_form': customer_po_form,
}
return render(request, 'client/add_po.html', context)
I am not sure how to handle it from here. Essentially, "customer_company" has a .pk, and i somehow want to capture that pk and populate it inside textfield in template when the company is selected.
inside here
<textarea
id="customer_bill_to_txt"
rows="5"
class="form-textarea">
{{ customer_po_form.order_bill_to }} </textarea>
I have tried to do it from forms.py by attempting to capture billing and shipping information
but this only populates class information inside textarea, nothing from client billing/shipping.
class ClientPOForm(forms.ModelForm):
:
:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Set the initial values for billing_address and shipping_address fields
if self.instance.pk:
billing_address = self.instance.order_bill_to.get_formatted_billing_address()
shipping_address = self.instance.order_ship_to.get_formatted_shipping_address()
self.initial['billing_address'] = billing_address
self.initial['shipping_address'] = shipping_address
i also tried using ajax from views and inside template..
views.py
def get_client_addresses(request):
if request.method == 'POST' and request.is_ajax():
client_id = request.POST.get('client_id')
client = get_object_or_404(Client, pk=client_id)
billing_address = get_object_or_404(ClientBillingAddress, client=client)
shipping_address = get_object_or_404(ClientShippingAddress, client=client)
data = {
'billing_address': billing_address.get_formatted_address(),
'shipping_address': shipping_address.get_formatted_address(),
}
return JsonResponse(data)
else:
return JsonResponse({'error': 'Invalid request'})
template.html
$(document).ready(function() {
// Add an event listener to the customer select field
$('#id_customer_company').change(function() {
// Get the selected customer ID
var customerId = $(this).val();
// Make an AJAX request to get the billing and shipping addresses for the selected customer
$.ajax({
url: `/get_client_addresses/${customerId}/`,
type: 'GET',
dataType: 'json',
success: function(data) {
// Update the textareas with the retrieved addresses
$('#customer_bill_to_txt').val(data.billing_address);
$('#customer_ship_to_txt').val(data.shipping_address);
},
error: function(xhr, status, error) {
console.log(xhr.responseText);
}
});
});
});
I am fairly sure I am missing something, but so far, I wasn't able to get the bill_To and ship_to to populate inside the form when selecting company_name. If anyone can help, I would greatly appreciate it.