Open Ryang20718 opened 7 months ago
Hello @Ryang20718
you need to create a QBDTask
to sync your Model object to
This is a snippet pulled from my production code
# Add something like this to your post_save signal.
QBDTask.objects.update_or_create(
qb_operation=QUICKBOOKS_ENUMS.OPP_MOD,
qb_resource=QUICKBOOKS_ENUMS.RESOURCE_CUSTOMER_POS,
object_id=instance.id,
content_type=ContentType.objects.get_for_model(instance),
realm_id=YOUR_REALM_ID,
)
it should call you ModelClass.to_qbd_obj()
Something Extra
you may also want to add something
like this to handle the signal coming back from QB in your settings.py
if you want to get qbd_object_id propagated into your DB
QBWC_SETTINGS = {
"LOCAL_MODEL_CLASSES": {
"CustomerPOS": "Inventory.models.Customer",
"ItemInventoryPOS": "Inventory.models.Sku",
"VoucherPOS": "Inventory.models.OrderVoucher",
},
"RESPONSE_PROCESSORS": (),
"POS_RESPONSE_PROCESSORS": (
"Inventory.qbd.ItemInventoryResponseProcessor",
"Inventory.qbd.SkuAddResponseProcessor", # Custom one I made
"django_quickbooks.processors.VoucherQueryResponseProcessor", # Default one
),
}
notice that `POS_RESPONSE_PROCESSORS` are only in my fork because it supports QBPOS
you can use `RESPONSE_PROCESSORS` for QB Finance
let me know if you find this helpful or if you need more help
@hassaanalansary Really appreciate the help here! If you could provide some pointers on where to place the QBDTask in relationship to a class, I think that would clear up all my confusion!
so I would need to create a class in order to leverage the customer/invoice classes
i.e if I have this in my models.py
class Customer(QBDModelMixin):
first_name = models.CharField(max_length=255, null=True)
last_name = models.CharField(max_length=255, null=True)
email = models.CharField(max_length=255, blank=True, null=True)
phone = models.CharField(max_length=10)
street = models.CharField(max_length=255, blank=True, null=True)
zip = models.CharField(max_length=255, blank=True, null=True)
city = models.CharField(max_length=255, blank=True, null=True)
state = models.CharField(max_length=255, blank=True, null=True)
def __str__(self):
return f'{self.first_name} {self.last_name}'
def to_qbd_obj(self, **fields):
from django_quickbooks.objects import Customer as QBCustomer
# map your fields to the qbd_obj fields
return QBCustomer(Name=self.__str__(),
IsActive=True,
Phone=self.phone,
)
@classmethod
def from_qbd_obj(cls, qbd_obj):
# map qbd_obj fields to your model fields
return cls(
first_name=qbd_obj.Name,
phone=qbd_obj.Phone,
qbd_object_id=qbd_obj.ListID,
qbd_object_version=qbd_obj.EditSequence
)
in my views.py,
I can instantiate the object
from .models import Customer
sample_customer = Customer(
first_name="John",
last_name="Doe",
email="john.doe@example.com",
phone="1234567890",
street="123 Main St",
zip="12345",
city="Anytown",
state="CA"
)
However, based on your snippet above, where am I supposed to place the following? I understand the realm ID is based on the QWC, but what object is instance supposed to be? is that supposed to be the instance of the object?
QBDTask.objects.update_or_create(
qb_operation=QUICKBOOKS_ENUMS.OPP_MOD,
qb_resource=QUICKBOOKS_ENUMS.RESOURCE_CUSTOMER_POS,
object_id=instance.id,
content_type=ContentType.objects.get_for_model(instance),
realm_id=YOUR_REALM_ID,
)
Instance is supposed to be Customer
in your case
so If I have the following Customer
class defined above in models.py
and the following in views.py
, navigating to the URL to invoke hello
should add a task to the QBWC?
from django.shortcuts import render
from django.http import HttpResponse
from django_quickbooks.models import QBDTask
from django_quickbooks import QUICKBOOKS_ENUMS
from django.contrib.contenttypes.models import ContentType
from django_quickbooks.services.invoice import InvoiceService
from django_quickbooks.services.customer import CustomerService
from qbPy.models import Customer
def create_customer():
sample_customer = Customer(
first_name="John",
last_name="Doe",
email="john.doe@example.com",
phone="1234567890",
street="123 Main St",
zip="12345",
city="Anytown",
state="CA"
)
QBDTask.objects.update_or_create(
qb_operation=QUICKBOOKS_ENUMS.OPP_MOD,
qb_resource=QUICKBOOKS_ENUMS.RESOURCE_CUSTOMER,
object_id=sample_customer.id,
content_type=ContentType.objects.get_for_model(sample_customer),
realm_id="073924d0-30a1-4e53-b8ce-121f184ae6d3",
)
def hello(request):
create_customer()
return HttpResponse("Tried creating a customer")
Assuming QuickBooks Desktop is Auth'd and connected to QBWC, if I were to invoke the app via update selected
in QBWC, I should be seeing the customer appear in QB desktop right? Currently I'm not quite sure if this is correct or if I'm missing something? 😅
Yes, however your code doesn't really create the customer in the database You should .save() And make sure you have redis working
ah, I see
I changed django_quickbooks to use redismanager rather than rabbit mq in the settings and have a redis server running.
I think I'm getting closer to reaching the final step of creating a customer programatically ❗ However, parsing the XML response results in an exception when trying to dynamically import... is this expected?
I noticed 2 things You are trying to perform an action on the Customer resource in QB but you didn't provide django_quiclbooks with the mapping between your Customer model and qb Cusotmer model.
You need to add 'Customer' to your LOCAL_MODEL_CLASSES in settings.py And you are issuing qb_operation=QUICKBOOKS_ENUMS.OPP_MOD on a non existent customer
Added a check when importing
if "." not in val:
return
and switching the enums to OP_ADD worked :). Appreciate all the help!
Sorry to abuse this issue for an unrelated question; what steps would I need to take to query existing customers already in quickbooks that I haven't added from this django app? (I assume sending an XML to query should be sufficient, but is there an example on how to do so?)
sorry missed your message: I imagine you have figured it out by now
you can do so by add QBDTask for qb_operation=QUICKBOOKS_ENUMS.OPP_QR to the Resource that you want to query
If you want, can you please open a PR with documentation about the issues that you have faced and how you have fixed them? This will others trying to use this project.
If you want, can you please open a PR with documentation about the issues that you have faced and how you have fixed them? This will others trying to use this project.
Definitely, I can do that. Hoping to do that once I confirm I have a solid understanding of querying as well 😓
you can do so by add QBDTask for qb_operation=QUICKBOOKS_ENUMS.OPP_QR to the Resource that you want to query
here's what I did.
I created a customer from quickbooks UI and then wanted to see if I could fetch that object via a query. Query is below. I received an XML response. However, is this right? I assume after reading the xml, I can just convert the xml to the actual object?
def query_customer():
print("QUERYING CUSTOMER")
sample_customer = CustomerModel(
first_name="john",
last_name="doe33",
email="john.doe@example.com",
phone="1234567890",
street="123 Main St",
zip="12345",
city="Anytown",
state="CA"
)
sample_customer.save()
res = QBDTask.objects.update_or_create(
qb_operation=QUICKBOOKS_ENUMS.OPP_QR,
qb_resource=QUICKBOOKS_ENUMS.RESOURCE_CUSTOMER,
object_id=sample_customer.id,
content_type=ContentType.objects.get_for_model(sample_customer),
realm_id="af1a9c3c-fdbc-45a2-9b2d-57646df3dc19",
)
print("FINISHED QUERYING", sample_customer)
What's not clear to me is:
when you issue the query
task. QB desktop will send you the customers in a response
all responses are handled ResponseProcessor
refer to my first comment
you need to define RESPONSE_PROCESSORS
in your settings.py
either use the builtin processors or you can define one and use it
"RESPONSE_PROCESSORS": (),
this is the builtin customers response processors
class CustomerQueryResponseProcessor(ResponseProcessor, ResponseProcessorMixin):
resource = QUICKBOOKS_ENUMS.RESOURCE_CUSTOMER
op_type = QUICKBOOKS_ENUMS.OPP_QR
local_model_class = LocalCustomer
obj_class = Customer
def process(self, realm):
cont = super().process(realm)
if not cont:
return False
for customer_ret in list(self._response_body):
customer = self.obj_class.from_lxml(customer_ret)
local_customer = None
if customer.ListID:
local_customer = self.find_by_list_id(customer.ListID)
if not local_customer and customer.Name:
local_customer = self.find_by_name(customer.Name)
if local_customer:
self.update(local_customer, customer)
else:
self.create(customer)
return True
you can inherit from it or define your own
if you have more questions you can send me an email, I will be more than happy to get in a meeting with you.
Hi there! XML/QB noob here, this thread has already been super helpful in programmatic customer creation. Would you mind also posting your solution to creating an invoice?
I see there's classes such as Customer, Invoice which I presume are created for common use cases.
Say my goal is to programmatically create a customer/invoice, is there a quickstart guide for doing so?
I.e once I'm authed with QB desktop, is there a way to create a customer and send that request to the webconnector to process? either via raw xml or a model?
If yall could post a short snippet on either this README example https://github.com/weltlink/django-quickbooks#implementation
or provide guidance on how I would add this customer to the QBD Task queue, that would be greatly appreciated! Trying to understand what the django_quickbooks.services is used for....