How to initiate a step back with editting functionality in viewflow - django-viewflow

I'm currently trying to recreate this flow:
So far, my flow only goes in 1 direction, there is no option for going back a step back, let's say Person 'b' spots a mistake keyed in by person 'a', I wish to be able to allow person 'b' to send the task back to person 'a' so that they can 'edit' their entry. However, there doesn't seem to be an 'editing' function in view flow, is there a reason for this?
flows.py
class Pipeline(Flow):
process_class = PaymentVoucherProcess
lock_impl = lock.select_for_update_lock
#process starts here
start = flow.Start(
PVStartView,
task_title="Processing New Voucher"
).Permission("cash.can_start_voucher"
).Next(this.approve_by_preparer)
#preparer will approve
approve_by_preparer = flow.View(
UpdateProcessView,
form_class=PreparerApproveForm,
task_title="Approval By Preparer"
).Assign(lambda act: act.process.assign_preparer
).Permission("cash.preparer"
).Next(this.documents)
#preparer will upload supporting documents
documents = flow.View(
UploadView,
task_title="Recieving Supporting Documents"
).Assign(lambda act: act.process.assign_preparer
).Permission("cash.preparer"
).Next(this.preparer)
#preparer will sign
preparer = flow.View(
PreparerSignature,
task_title="Signature By Preparer"
).Assign(lambda act: act.process.assign_preparer
).Permission("cash.preparer"
).Next(this.approve_by_verifier)
#system check point
check_treasury = flow.If(
cond=lambda act: act.process.preparer,
task_title="Processing",
).Then(this.approve_by_verifier).Else(this.end)
#verifier will sign
approve_by_verifier = flow.View(
UpdateProcessView,
form_class=VerifierApproveForm,
task_title="Approval By Verifier"
).Assign(lambda act: act.process.assign_verifier
).Permission("cash.verifier"
).Next(this.verifier)
#there is more but not relevant
I understand that there are some 'undo' mixin views but I'm not sure how it is being implemented in my case and how everything is orchestrated, please help!

I still have no clue on how to properly do this. But here is what i did for the meantime :
flows.py
class Pipeline(Flow):
process_class = PaymentVoucherProcess
lock_impl = lock.select_for_update_lock
#process starts here
start = flow.Start(
PVStartView,
task_title="Processing New Voucher"
).Permission("cash.can_start_voucher"
).Next(this.approve_by_preparer)
#preparer will approve
approve_by_preparer = flow.View(
UpdateProcessView,
form_class=PreparerApproveForm,
task_title="Approval By Preparer"
).Assign(lambda act: act.process.assign_preparer
).Permission("cash.preparer"
).Next(this.check_preparer)
#system check point
check_preparer = flow.If(
cond=lambda act: act.process.preparer,
task_title="Processing",
).Then(this.documents).Else(this.roll_back)
roll_back = flow.Handler(this.roll_back_call).Next(this.approve_by_preparer)
#preparer will upload supporting documents
documents = flow.View(
UploadView,
task_title="Recieving Supporting Documents"
).Assign(lambda act: act.process.assign_preparer
).Permission("cash.preparer"
).Next(this.preparer)
#preparer will sign
preparer = flow.View(
PreparerSignature,
task_title="Signature By Preparer"
).Assign(lambda act: act.process.assign_preparer
).Permission("cash.preparer"
).Next(this.approve_by_verifier)
#verifier will sign
approve_by_verifier = flow.View(
UpdateProcessView,
form_class=VerifierApproveForm,
task_title="Approval By Verifier"
).Assign(lambda act: act.process.assign_verifier
).Permission("cash.verifier"
).Next(this.check_verifier)
#system check point
check_verifier = flow.If(
cond=lambda act: act.process.verifier,
task_title="Processing"
).Then(this.verifier).Else(this.roll_back)
#bunch of code here and inbetween
end = flow.End()
def roll_back_call(self, activation):
esig = ESignatures.objects.filter(paymentVoucherProcess = activation.process).filter(voided = False)
docu = Attachment.objects.filter(paymentVoucherProcess = activation.process).filter(voided = False)
if len(esig) > 0 :
for sig in esig:
sig.voided = True
sig.save()
if len(docu) > 0 :
for doc in docu:
doc.voided = True
doc.save()
activation.process.preparer = False
activation.process.verifier = False
activation.process.treasury = False
activation.process.director = False
Basically what is happening in this Roll back handler is that when the program figures out that Person 'B' disapproves , it will run the roll_back code which searches for the process and 'refreshes' it , marking all foreign relations to void and removing all prior approvals. It is really super unorthodox and hacky , but for the meantime it works , please let me know you guys comments and do share with me your answers.
I still havent figured out how exactly one can 'edit' or 'cancel' or 'reassign' his tasks , the documentations don't have much and im really surprised people don't face such issues as there is a severe lack of content out there .
Anyways i hope to see some constructive feed back!

Undo task flow is part of admin functionality, and not desined to be used by usual cases. If you need to edit a process data by user a, you could simple create edit task for user a
check = flow.If().Then(...).Else(this.edit_by_a)
edit_by_a = flow.View().Assign(this.start.owner).Next(this.check)

Related

How to use same django filter class(filters.py) in two different views

I have a filter class defined below.
filters.py
class CTAFilter(django_filters.FilterSet):
id = django_filters.NumberFilter(label="DSID")
class Meta:
model = CTA
fields = ['id', 'EmailID','id','Shift_timing']
Now I want to use this CTAFilter in normal template(table data)view and in download views.
I have observed that It is working fine for normal render view but when I am using it in my download views it is not working and I am getting all model data in the .xls file.
Please find the below questions which I have posted.
how to use Django filtered class data to 2 seperate view
I am not able to resolve this problem I have tried to check if I can define it globally so that the filter will work for all views(like RESTAPI).
Is there any way I can make my download view as a child view class of normal render view so that I will use the below code from the parent view(as it is working fine)?
cta_list = CTA.objects.all()
cta_filter = CTAFilter(request.GET, queryset=cta_list) allcta = cta_filter.qs
A>Normal View where the filter is working fine.
def retrievecta_view(request):
if request.method == 'GET':
allcta = CTA.objects.all()
allcta1 = allcta
allctagen = allcta1.filter(Shift_timing__exact='General')
allctamor = allcta1.filter(Shift_timing__exact='Morning')
allctseve = allcta1.filter(Shift_timing__exact='Evening')
allctatotal = allcta1.filter(Shift_timing__exact='Total')
# For filtering using 'django_filters',
cta_list = CTA.objects.all()
cta_filter = CTAFilter(request.GET, queryset=cta_list)
allcta = cta_filter.qs
paginator = Paginator(allcta, 50)
page_number = request.GET.get('page')
try:
allcts = paginator.page(page_number)
except PageNotAnInteger:
allcts = paginator.page(1)
except EmptyPage:
allcts = paginator.page(paginator.num_pages)
return render(request, 'abcd/cta.html', {'allcta': allcta, 'cta_filter': cta_filter, 'allcta1': allcta1,
'allctagen': allctagen, 'allctamor': allctamor,
'allctaeve': allctaeve,
'allctatotal': allctatotal})
b> Download view where I am trying to use the same filter but it is giving me all records.
def exportcts_data(request):
response = HttpResponse(content_type='application/ms-excel')
response['Content-Disposition'] = 'attachment; filename="CTA_ShiftTiming.xls"'
wb = xlwt.Workbook(encoding='utf-8')
ws = wb.add_sheet('CTS_ShiftChange Data') # this will make a sheet named Users Data
# Sheet header, first row
row_num = 0
font_style = xlwt.XFStyle()
font_style.font.bold = True
columns = ['id','idk','Shift_timing','EmailID','Vendor_Company','Project_name','SerialNumber','Reason','last_updated_time']
for col_num in range(len(columns)):
ws.write(row_num, col_num, columns[col_num], font_style) # at 0 row 0 column
# Sheet body, remaining rows
font_style = xlwt.XFStyle()
cts_list = CTA.objects.all()
cts_filter = CTAFilter(request.GET, queryset=cts_list)
allcts = cts_filter.qs
rows = allcts.values_list('id', 'idk', 'Shift_timing', 'EmailID', 'Vendor_Company', 'Project_name',
'SerialNumber', 'Reason', 'last_updated_time')
for row in rows:
row_num += 1
for col_num in range(len(row)):
ws.write(row_num, col_num, row[col_num], font_style)
wb.save(response)
return response
I'm not quite following why you want to have separate view for downloads which ultimately should be rendering the same data as the normal view if they are using the same filter. Maybe it is just my misunderstanding so I'm not sure if this will help you but let's see.
First off let me explain a little background. This is a task management application and in there I have an html page where the person logged in can view all of their completed tasks. (Nice and simple.) However the user may have tasks from many different projects so I have created a dropdown list that allows them to filter by a single project. They may also want to only see a specific period of tasks so I have allowed them to set a date range by providing a start and end date. (Nothing startling or earth shattering here.) Once the parameters are set, the user clicks a search button and the filtered results are displayed. The page also has an Export button which downloads the results of the filtered list to a .xls spreadsheet.
So how do I do this? Well first of all, I am using Django-Tables2 for rendering my tables. I simple predefine the table in tables.py and throw it the data I want from my views and it takes care of everything. Therefore my view code is minimal and very simple and looks like this.
from django_tables2.export.export import TableExport
from .tables import CompletedTable
def completedlist(request, page='0', name=''):
#Check to see if we have clicked a button inside the form
if request.method == 'POST':
return redirect ('tasks:tasklist')
else:
# Pre-filtering of user and Master = True etc is done in the MasterListFilter in filters.py
# Then we compile the list for Filtering by.
f = CompletedListFilter(request.GET, queryset=Task.objects.all(),request=request)
# Then we apply the complete list to the table, configure it and then render it.
completedtable = CompletedTable(f.qs)
rows = len(completedtable.rows)
if int(page) > 0:
RequestConfig(request, paginate={'page': page, 'per_page': 10}).configure(completedtable)
else:
RequestConfig(request, paginate={'page': 1, 'per_page': 10}).configure(completedtable)
export_format = request.GET.get('_export', None)
if TableExport.is_valid_format(export_format):
exporter = TableExport(export_format, completedtable)
return exporter.response('Completed Tasks.{}'.format(export_format))
return render (request,'tasks/completedlist.html',{'completedtable': completedtable, 'filter': f, 'rows': rows})
As you can see, every time the user hits either the search or export buttons, I am recompiling the queryset in variable f with the following line:
f = CompletedListFilter(request.GET, queryset=Task.objects.all(),request=request)
I have predefined the .xls format in the html page with this code:
<button class="btn btn-primary btn-xs" name="_export" value="xls" type="submit">Export</button>
So then I can test to see if the user clicked the Export button or not by getting the value of _export from the request like this:
export_format = request.GET.get('_export', None)
If the user did not click the export button, export_format will default to none. If they did, it will be .xls as defined in the html. Then I simply either export the data in line with the filters set by the user or I render the page with the same filtered list of data like this:
if TableExport.is_valid_format(export_format):
exporter = TableExport(export_format, completedtable)
return exporter.response('Completed Tasks.{}'.format(export_format))
return render (request,'tasks/completedlist.html',{'completedtable': completedtable, 'filter': f, 'rows': rows})
So there you have it. As you say your filter is working for the normal view I have not detailed my filter as that would seem to be unnecessary.
Maybe this solution is too simplistic for your requirements and yes, before I get shot down by other developers, there are several limitations, such as 'What if the user wants to use something other than .xls?' or 'What if they want to export more than one Project at a time?' Like everything, there is always room for improvement but when I'm bashing my head with an issue, I often find it helps to strip things back to basics and see what comes from that.

Change Item Price when Creating Purchase Order via PHP in Netsuite

I am attempting to create open purchase orders in Netsuite during a system migration using Netsuite's PHP Toolkit for 2019_2. I am able to create the purchase order and line items without a problem, but I cannot figure out how to change the item cost on the line item. Setting the line item extended total works, but setting the rate field does not. Can anyone shed any light on why the price is not being set?
I've tried both $poi->item->rate = "5.00"; and $poi->item->rate = 5.00; with no success. The documentation says that this field is a string.
https://www.netsuite.com/help/helpcenter/en_US/srbrowser/Browser2019_2/schema/other/purchaseorderitem.html
$service = new NetSuiteService();
$po = new PurchaseOrder();
$po->tranId = 'PO111111';
$po->tranDate = '2019-03-27T00:00:00';
$po->approvalStatus = new RecordRef();
$po->approvalStatus->internalId = 2;
$po->entity = new RecordRef();
$po->entity->externalId = 'VENDORNAME';
// Create PO Item
$poi = new PurchaseOrderItem();
$poi->item = new RecordRef();
$poi->item->externalId = 'ITEMNUMBER';
$poi->item->rate = "5.00";
$poi->quantity = 10;
$po->itemList = new PurchaseOrderItemList();
$po->itemList->item = array($poi);
$request = new AddRequest();
$request->record = $po;
$addResponse = $service->add($request);
if (!$addResponse->writeResponse->status->isSuccess) {
echo "ADD ERROR";
print_r($addResponse);
} else {
echo "ADD SUCCESS, id " . $addResponse->writeResponse->baseRef->internalId;
}
Set the rate on the purchase order line instead of trying to do it on the item record.
$poi->rate = "5.00";

Coded UI is identifying the control but not performing any action

I have written the below code,
WinWindow w = new WinWindow();
w.SearchProperties[WinWindow.PropertyNames.Name] = "Card Action";
w.SearchProperties[WinWindow.PropertyNames.ClassName] = "#32770";
w.SearchProperties[WinButton.PropertyNames.ControlType] = "Window";
WinWindow w2 = new WinWindow(w);
w2.SearchConfigurations.Add(SearchConfiguration.AlwaysSearch);
w2.SearchConfigurations.Add(SearchConfiguration.VisibleOnly);
w2.SearchProperties[WinWindow.PropertyNames.ControlType]="Window";
w2.SearchProperties[WinWindow.PropertyNames.Name] = "Insert Card";
w2.SearchProperties[WinWindow.PropertyNames.ControlId] = "2032";
WinButton b1 = new WinButton(w2);
b1.SearchConfigurations.Add(SearchConfiguration.AlwaysSearch);
b1.SearchConfigurations.Add(SearchConfiguration.VisibleOnly);
b1.SearchProperties[WinButton.PropertyNames.ControlType] = "Button";
b1.SearchProperties[WinButton.PropertyNames.ClassName] = "Button";
b1.SearchProperties[WinButton.PropertyNames.Name] = "Insert Card";
b1.DrawHighlight();
Mouse.Click(b1);
It has identified the control but unable to perform click action.
Error:
Test method CodedUITestMethod1 threw exception:
Another control is blocking the control. Please make the blocked control visible and retry the action. Additional Details: TechnologyName: 'MSAA' Name: 'Insert Card' ClassName: 'Button' ControlType: 'Window'
Output Screenshot
https://i.stack.imgur.com/mhbgj.png
Please help me to sort out this problem.
Thanks in advance!

How do I disable products in Kentico 10?

I am performing CRUD operations for products of e-commerce site in kentico 10.I can add and update products using below API
SKUInfoProvider.SetSKUInfo(updateProduct);
Also there is an API for deleting product
SKUInfoProvider.DeleteSKUInfo(updateProduct);
But I do not wish to delete the product from database,rather just disable them so that they do not show up to the end users and still stay in the database .
This are the SKU Objects for the product :
var sku = new SKUInfo
{
//SKUName = Convert.ToString(dr["SHORT_DESCRIPTION"]).Trim('"') + " (" + Convert.ToString(dr["MANUFACTURER_PART_NUMBER"]) + ")",
SKUName = Convert.ToString(dr["MANUFACTURER_PART_NUMBER"]),
SKUDescription = Convert.ToString(dr["TECHNICAL_SPECIFICATIONS"]).Trim('"'),
SKUShortDescription = Convert.ToString(dr["SHORT_DESCRIPTION"]).Trim('"'),
SKUPrice = ValidationHelper.GetDouble(dr["RESELLER_BUY_INC"], 0),
SKURetailPrice = ValidationHelper.GetDouble(dr["RRP_INC"], 0),
SKUEnabled = true,
SKUSiteID = siteId,
SKUProductType = SKUProductTypeEnum.Product,
SKUManufacturerID = manufacturer.ManufacturerID,
SKUDepartmentID = department.DepartmentID,
SKUHeight = 100,
SKUWidth = 100,
SKUAvailableItems = 1,
SKUSellOnlyAvailable = true
};
I tried to set SKUEnabled as false but still user can see the product.So, is there any property to disable products ?
How are you displaying the Sku? If it's a repeater, you may need to filter by the "SKUEnabled = 1" in your where condition.
Another option is if the Product has a Page (it's not a stand alone sku) you can unpublish the page itself.
Well... A user doesn't see products per say - he sees pages that are connected to your SKUs/products. When you disable the SKU - the page is still visible, but (if I am not mistaken) "Add To Cart" is not shown. You need to unpublish product pages. You need to set DocumentPublishTo of the document to some date before for ex:
ProductNode.SetValue("DocumentPublishTo", DateTime.Now.AddDays(-1));

Two Web Parts connection doesn't work when set programmatically

I've a SharePoint 2010 page with a list. The list has several items and a field named "Department" and must filter items based on user's department value retrieved from user profile.
To do this I've created a feature which upon activation adds UserContextFilterWebPart to the page and makes connection between UserContextFilterWebPart and XsltListViewWebPart. After the feature is activated I can see in the page design mode that connection is established but the list gets empty. Then I open web part's menu, choose "Connections" then "Send Filter Values To" and click "List1". When dialog appears I do nothing but only click "Finish" button and it begins to work fine. Can anybody please explain me why the connection begins to work only if I do that manual extra action? What must be done to fix?
I tried different way when List.Views[0].Query property is set to appropriate CAML query and it also works fine. But I'm told that it's not a good approach because of performance and parallel tasks issues. Is it really bad course of action?
Below is the code for 2 different approaches.
Thanks in advance!
1-s variant with UserContextFilterWebPart:
SPSite Site = new SPSite(URL);
SPWeb Web = Site.OpenWeb();
SPLimitedWebPartManager WPM = Web.GetLimitedWebPartManager(URL, PersonalizationScope.Shared);
XsltListViewWebPart List = WPM.WebParts[0] as XsltListViewWebPart;
UserContextFilterWebPart UCFWP = new UserContextFilterWebPart();
UCFWP.Title = "Current User Filter";
UCFWP.AllowEdit = true;
UCFWP.FilterName = "Current User";
UCFWP.SendEmptyWhenNoValues = true;
UCFWP.AllowClose = true;
UCFWP.ExportMode = WebPartExportMode.All;
UCFWP.AllowConnect = true;
UCFWP.AllowHide = true;
UCFWP.ProfilePropertyName = "Department";
UCFWP.ValueKind = UserContextFilterValueKind.ProfileValue;
UCFWP.ZoneID = "Main";
WPM.AddWebPart(UCFWP, UCFWP.ZoneID, 1);
WPM.SaveChanges(UCFWP);
ConsumerConnectionPointCollection consumerConnections = WPM.GetConsumerConnectionPoints(List);
ConsumerConnectionPoint addConsumerConnPoint = consumerConnections["DFWP Filter Consumer ID"];
ProviderConnectionPointCollection providerConnections = WPM.GetProviderConnectionPoints(UCFWP);
ProviderConnectionPoint addProviderConnPoint = providerConnections["ITransformableFilterValues"];
TransformableFilterValuesToParametersTransformer trans = new TransformableFilterValuesToParametersTransformer();
trans.ConsumerFieldNames = new string[] { "Department" };
trans.ProviderFieldNames = new string[] { "Department" };
SPWebPartConnection newConnection = WPM.SPConnectWebParts(UCFWP, addProviderConnPoint, List, addConsumerConnPoint, trans);
WPM.SPWebPartConnections.Add(newConnection);
2-nd variant with CAML query (intended to be used not in a feature but in a web part):
SPSite Site = new SPSite(URL);
SPWeb Web = Site.OpenWeb();
SPLimitedWebPartManager WPM = Web.GetLimitedWebPartManager(URL, PersonalizationScope.Shared);
XsltListViewWebPart List = WPM.WebParts[0] as XsltListViewWebPart;
SPUser CurrentUser = Web.CurrentUser;
SPServiceContext context = SPServiceContext.GetContext(Site);
UserProfileManager upm = new UserProfileManager(context, false);
UserProfile up = upm.GetUserProfile(CurrentUser.RawSid);
String UserDepartment = up["Department"].Value.ToString();
SPView ListView = Web.Lists["List1"].Views[0];
ListView.Query = "<Where><Eq><FieldRef Name='Department' /><Value Type='Text'>" + UserDepartment + "</Value></Eq></Where>";
ListView.Update();
I had a similar problem of connecting two web parts. I found the answer here: http://kvdlinden.blogspot.dk/2011/02/programmatically-connect-two.html
Note that that post describes how to do it with two XsltListViewWebParts. In order to use it in your case I suggest that you:
Create the connection manually,
Use PowerShell to get a SPLimitedWebPartManager for the page,
Use the manager to iterate through the manager.SPWebPartConnections,
And find the ProviderConnectionPointID for your connection,
Use that ID in the code shown in the post.
Also remember to setup the transformer - you can find this also from the SPWebPartConnections.
Next time you activate your feature you should have a connection equal to the one you made by hand.

Resources